package cn.com.nes.site.module.service.iot;

import cn.com.nes.common.annotation.OpeTransactional;
import cn.com.nes.common.util.CompareStringMatch;
import cn.com.nes.common.util.HexUtils;
import cn.com.nes.common.util.ParameterUtil;
import cn.com.nes.common.util.iot.ModbusProtocolUtil;
import cn.com.nes.common.util.listener.IdUtil;
import cn.com.nes.mybatis.agent.ope.entity.*;
import cn.com.nes.mybatis.agent.ope.mapper.*;
import cn.com.nes.mybatis.agent.system.entity.TAgentFacturer;
import cn.com.nes.mybatis.agent.system.mapper.TAgentFacturerMapper;
import cn.com.nes.site.entity.DataReq;
import cn.com.nes.site.entity.PageData;
import cn.com.nes.site.entity.dto.DataItemDetailDTO;
import cn.com.nes.site.entity.dto.DataItemsForDropdownDTO;
import cn.com.nes.site.entity.dto.FunctionDetailDTO;
import cn.com.nes.site.entity.dto.GetStandardDataItemsDTO;
import cn.com.nes.site.entity.em.DataType;
import cn.com.nes.site.entity.em.RegisterTypeEnum;
import cn.com.nes.site.entity.em.StandardDataItemEnum;
import cn.com.nes.site.entity.iot.ControlCommandForEquipmentControlParam;
import cn.com.nes.site.entity.iot.NesProtocolDataSet;
import cn.com.nes.site.entity.iot.RegisterDataItem;
import cn.com.nes.site.entity.vo.iot.ProtocolFaultDataItemRelationVo;
import cn.com.nes.site.service.iot.EquipmentControlParamService;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.HexUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.script.JavaScriptEngine;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.defaults.DefaultSqlSessionFactory;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.script.ScriptException;
import java.math.RoundingMode;
import java.util.*;

import static cn.com.nes.common.util.CRC.getCRCByTbl;

@Slf4j
@Service
public class ProtocolService {

    @Resource
    private IsDeviceProtocolInfoMapper deviceProtocolInfoMapper;

    @Resource
    private IsProtocolQueryCommandMapper queryCommandMapper;

    @Resource
    private IsDeviceProtocolDataItemInfoMapper dataItemInfoMapper;

    @Resource
    private IsDataItemFieldMapper dataItemFieldMapper;

    @Resource
    private IsDataItemTemplateInfoMapper templateInfoMapper;

    @Resource
    private IsProtocolControlCommandMapper controlCommandMapper;

    @Resource
    private IsEquipmentFunctionDetailInfoMapper functionDetailInfoMapper;

    @Resource
    private IsVersionMapper versionMapper;

    @Resource
    private DefaultSqlSessionFactory sqlSessionFactory;

    @Resource
    private AStandardDataItemMapper standardDataItemMapper;

    @Resource
    private RegisterTemplateMapMapper registerTemplateMapMapper;

    @Resource
    IsDictValueInfoMapper dictValueInfoMapper;

    @Resource
    TAgentFacturerMapper agentFacturerMapper;

    @Resource
    private FaultDataItemRelationMapper faultDataItemRelationMapper;

    @Resource
    EquipmentControlParamMapper equipmentControlParamMapper;

    @Resource
    private EquipmentControlParamService equipmentControlParamService;

    @Resource
    private EquipmentControlParamCustomMapper equipmentControlParamCustomMapper;

    @OpeTransactional
    public PageData saveProtocol(DataReq req) {

        final String companyType = req.getUinfo().getCompanyType();
        String manufactureId = "01".equals(companyType) ? req.getUinfo()
                .getNes_companyId() : ParameterUtil.getString(req, "manufactureId");

        if (StringUtils.isBlank(manufactureId)) {
            manufactureId = req.getUinfo().getNes_companyId();
        }
        if (StringUtils.isBlank(manufactureId)) {
            return PageData.error("厂商id为空,不能保存协议");
        }

        final String companyId = "01".equals(companyType) ? "" : req.getUinfo().getCompanyId();
        // 协议名称 必填
        String name = ParameterUtil.getString(req, "name");
        // 波特率 必填
        String bandRate = ParameterUtil.getString(req, "bandRate");
        // 数据位 必填
        String dataBit = ParameterUtil.getString(req, "dataBit");
        // 校验位 必填
        String checkBit = ParameterUtil.getString(req, "checkBit");
        // 停止位 必填
        String stopBit = ParameterUtil.getString(req, "stopBit");
        // 协议ID
        String protocolId = ParameterUtil.getString(req, "protocolId");
        // 从站地址
        String slaveAddress = ParameterUtil.getString(req, "slaveAddress", "01");
        // 单条指令最大读取寄存器数 1-bit
        Integer max1BitRegisterReadCount = ParameterUtil.getInteger(req, "max1BitRegisterReadCount", 80);
        // 单条指令最大读取寄存器数 16-bit
        Integer max16BitRegisterReadCount = ParameterUtil.getInteger(req, "max16BitRegisterReadCount", 8);
        // 从站地址必须为16进制
        if (slaveAddress.startsWith("0x") || slaveAddress.startsWith("0X")) {
            slaveAddress = slaveAddress.substring(2);
        }
        if (!slaveAddress.matches("^[\\da-fA-F]+$")) {
            return PageData.error("从站地址输入有误");
        }
        // 去掉前导0
        while (slaveAddress.indexOf("0") == 0 && slaveAddress.length() > 1) {
            slaveAddress = slaveAddress.substring(1);
        }
        if (slaveAddress.length() == 1) {
            slaveAddress = "0" + slaveAddress;
        }
        if (slaveAddress.length() > 2) {
            return PageData.error("从站地址超出范围");
        }

        // 01/02 命令最多读取2000个寄存器
        if (max1BitRegisterReadCount > 2000) {
            return PageData.error("01/02命令最多读取2000个寄存器");
        }
        // 03/04 命令最多读取125个寄存器
        if (max16BitRegisterReadCount > 125) {
            return PageData.error("03/04命令最多读取125个寄存器");
        }

        boolean changeSlaveAddress = false;
        boolean changeMax1BitRegisterReadCount = false;
        boolean changeMax16BitRegisterReadCount = false;
        final IsDeviceProtocolInfo protocol = new IsDeviceProtocolInfo();
        if (StrUtil.isNotBlank(protocolId)) {
            protocol.setProtocolId(protocolId);
            final IsDeviceProtocolInfo oldProtocol = deviceProtocolInfoMapper.selectOne(new LambdaQueryWrapper<>(protocol));
            Long id = oldProtocol.getId();
            protocol.setId(id);
            if (!oldProtocol.getSlaveAddress().equals(slaveAddress)) {
                changeSlaveAddress = true;
            }
            if (!oldProtocol.getMax1BitRegisterReadCount().equals(max1BitRegisterReadCount)) {
                changeMax1BitRegisterReadCount = true;
            }
            if (!oldProtocol.getMax16BitRegisterReadCount().equals(max16BitRegisterReadCount)) {
                changeMax16BitRegisterReadCount = true;
            }
        }

        protocol.setSlaveAddress(slaveAddress);
        protocol.setMax1BitRegisterReadCount(max1BitRegisterReadCount);
        protocol.setMax16BitRegisterReadCount(max16BitRegisterReadCount);
        protocol.setManufactureId(manufactureId);
        protocol.setAgentCompanyId(companyId);
        protocol.setName(name);
        protocol.setDataStart(0);
        protocol.setSerialPortDuration(0);
        protocol.setInvalidValue("");
        protocol.setIsPublic(0);
        protocol.setIsStart(1);
        protocol.setIsPlc(0);
        protocol.setIsDeleted(0);
        protocol.setCrOper(req.getUinfo().getNickName());
        final Date now = new Date();
        protocol.setGmtCreate(now);
        protocol.setGmtModified(now);
        protocol.setUpOper(req.getUinfo().getNickName());
        protocol.setSjly("hot_chain");
        protocol.setSingleDoubleBytes(0);
        protocol.setVersion(System.currentTimeMillis());
        protocol.setModifyRemarks("");
        protocol.setCtceProtocolId("");
        protocol.setQueryIntervalTime(10);
        protocol.setIsSendAgain(1);
        protocol.setWarningCnt(2);
        protocol.setWarningTime(15);
        protocol.setCtrlCmdType(1);
        protocol.setBandrate(bandRate);
        protocol.setDatabit(dataBit);
        protocol.setCheckbit(checkBit);
        protocol.setStopbit(stopBit);
        protocol.setProtocolType(0);
        protocol.setOvertime(5);
        protocol.setControlWaitTime(20);

        if (StrUtil.isBlank(protocolId)) {
            protocol.setProtocolId("dp_" + IdUtil.getIncreaseIdByNanoTime());
            deviceProtocolInfoMapper.insert(protocol);
        } else {
            deviceProtocolInfoMapper.updateById(protocol);
        }

        String username = req.getUinfo().getNickName();
        if (changeMax1BitRegisterReadCount) {
            queryCommandMapper.delete(new QueryWrapper<IsProtocolQueryCommand>().eq("protocol_id", protocolId)
                    .and(wrapper -> wrapper.likeRight("command", "__01")
                            .or()
                            .likeRight("command", "__02"))
                    );
        }
        if (changeMax16BitRegisterReadCount) {
            queryCommandMapper.delete(new QueryWrapper<IsProtocolQueryCommand>().eq("protocol_id", protocolId)
                            .and(wrapper ->
                                wrapper.likeRight("command", "__03")
                                        .or()
                                        .likeRight("command", "__04")
                            ));
        }

        if (changeMax1BitRegisterReadCount || changeMax16BitRegisterReadCount) {
            List<NesProtocolDataSet> inputList = new ArrayList<>();
            List<IsDeviceProtocolDataItemInfo> dataItemInfos = dataItemInfoMapper.selectList(new QueryWrapper<IsDeviceProtocolDataItemInfo>().select(IsDeviceProtocolDataItemInfo.ID, IsDeviceProtocolDataItemInfo.REGISTER_ADDRESS, IsDeviceProtocolDataItemInfo.REGISTER_TYPE, IsDeviceProtocolDataItemInfo.BYTE_LENGTH, IsDeviceProtocolDataItemInfo.DATA_TYPE_ENUM, IsDeviceProtocolDataItemInfo.BIT_PLACE, IsDeviceProtocolDataItemInfo.BIT_PLACE_END, IsDeviceProtocolDataItemInfo.TEMPLATE_ID, IsDeviceProtocolDataItemInfo.IS_READ_WRITE)
                    .eq(IsDeviceProtocolDataItemInfo.PROTOCOL_ID, protocolId)
                    .eq(IsDeviceProtocolDataItemInfo.IS_DELETED, 0));
            Set<String> commandSet = new HashSet<>();
            for (IsDeviceProtocolDataItemInfo dataItemInfo : dataItemInfos) {
                NesProtocolDataSet dataSet = new NesProtocolDataSet();
                dataSet.setDataItemInfo(dataItemInfo);
                RegisterDataItem registerDataItem = new RegisterDataItem();
                registerDataItem.setRegisterAddress(dataItemInfo.getRegisterAddress());
                registerDataItem.setRegisterType(dataItemInfo.getRegisterType());
                registerDataItem.setDataType(dataItemInfo.getDataTypeEnum());
                if (ObjUtil.isEmpty(dataItemInfo.getBitPlaceEnd())) {
                    if (ObjUtil.isEmpty(dataItemInfo.getBitPlace())) {
                        registerDataItem.setBitAddress(null);
                    } else {
                        registerDataItem.setBitAddress(String.valueOf(dataItemInfo.getBitPlace()));
                    }
                } else {
                    registerDataItem.setBitAddress(String.format("%s-%s", dataItemInfo.getBitPlace(), dataItemInfo.getBitPlaceEnd()));
                }
                registerDataItem.setRead(dataItemInfo.getIsReadWrite());
                dataSet.setRegisterDataItem(registerDataItem);
                inputList.add(dataSet);
            }
            Collections.sort(inputList);

            new ModbusProtocolUtil().generateQueryCommand(inputList, protocolId, username, slaveAddress, max1BitRegisterReadCount, max16BitRegisterReadCount, this, queryCommandMapper);

            for (NesProtocolDataSet dataSet : inputList) {
                if (dataSet.getQueryCommand() == null) {
                    continue;
                }
                IsDeviceProtocolDataItemInfo dataItemInfo = dataSet.getDataItemInfo();
                dataItemInfo.setGmtModified(now);
                dataItemInfo.setUpOper(username);
                dataItemInfoMapper.updateById(dataItemInfo);
                if (!commandSet.contains(dataSet.getQueryCommand().getQueryCommandId())) {
                    queryCommandMapper.insert(dataSet.getQueryCommand());
                    commandSet.add(dataSet.getQueryCommand().getQueryCommandId());
                }
            }
        }

        if (changeSlaveAddress) {
            if (!changeMax1BitRegisterReadCount && !changeMax16BitRegisterReadCount) {
                List<IsProtocolQueryCommand> queryCommands = queryCommandMapper.selectList(new QueryWrapper<IsProtocolQueryCommand>().select(IsProtocolQueryCommand.ID, IsProtocolQueryCommand.COMMAND, IsProtocolQueryCommand.RESPONSE_HEADER)
                        .eq(IsProtocolQueryCommand.PROTOCOL_ID, protocolId)
                        .eq(IsProtocolQueryCommand.IS_DELETED, 0));
                for (IsProtocolQueryCommand queryCommand : queryCommands) {
                    String command = queryCommand.getCommand();
                    String newCommand = slaveAddress + command.substring(2);
                    queryCommand.setCommand(newCommand);
                    queryCommand.setGmtModified(now);
                    queryCommand.setUpOper(username);
                    String responHeader = slaveAddress + queryCommand.getResponseHeader().substring(2);
                    queryCommand.setResponseHeader(responHeader);
                    queryCommandMapper.updateById(queryCommand);
                }
            }
            // 控制指令
            List<IsProtocolControlCommand> controlCommands = controlCommandMapper.selectList(new QueryWrapper<IsProtocolControlCommand>().select(IsProtocolControlCommand.ID, IsProtocolControlCommand.COMMAND, IsProtocolControlCommand.RELATION_DATA_ITEM_IDS, IsProtocolControlCommand.CONTROL_BIT_PLACE, IsProtocolControlCommand.CONTROL_BIT_PLACE_END, IsProtocolControlCommand.RESPONSE_HEADER)
                    .eq(IsProtocolControlCommand.PROTOCOL_ID, protocolId)
                    .eq(IsProtocolControlCommand.IS_DELETED, 0));
            for (IsProtocolControlCommand controlCommand : controlCommands) {
                String command = controlCommand.getCommand();
                String newCommand = slaveAddress + command.substring(2);
                controlCommand.setCommand(newCommand);
                controlCommand.setGmtModified(now);
                controlCommand.setUpOper(username);
                String responHeader = slaveAddress + controlCommand.getResponseHeader().substring(2);
                controlCommand.setResponseHeader(responHeader);
                controlCommandMapper.updateById(controlCommand);
            }
        }

        return PageData.ok();
    }

    public PageData getProtocol(DataReq req) {
        int pageId = req.getPageid();
        int pageSize = req.getPagesize();
        String protocolId = ParameterUtil.getString(req, "protocolId");
        String protocolName = ParameterUtil.getString(req, "protocolName");
        Integer hasNormalDataItem = ParameterUtil.getInteger(req, "hasNormalDataItem");
        final String companyType = req.getUinfo().getCompanyType();
        final String companyId = "00".equals(companyType) ? "" : "01".equals(companyType) ? "" : req.getUinfo()
                .getCompanyId();
        final String manufactureId = "00".equals(companyType) ? "" : "01".equals(companyType) ? req.getUinfo()
                .getNes_companyId() : ParameterUtil.getString(req, "manufactureId");

        final Page<Map<String, Object>> page = new Page<>(pageId, pageSize);
        final List<Map<String, Object>> result = deviceProtocolInfoMapper.getProtocols(page, protocolId, protocolName, hasNormalDataItem, manufactureId, companyId);
        page.setRecords(result);

        return PageData.ok(page);
    }

    public PageData saveQueryCommand(DataReq req) {
        // 命令ID
        String queryCommandId = ParameterUtil.getString(req, "queryCommandId");
        // 协议ID
        String protocolId = ParameterUtil.getString(req, "protocolId");
        // 命令名称
        String name = ParameterUtil.getString(req, "name");
        // 命令
        String command = ParameterUtil.getString(req, "command");
        // 采集周期
        Integer collectCycle = ParameterUtil.getInteger(req, "collectCycle");
        String slaveAddress = ParameterUtil.getString(req, "slaveAddress");
        String functionCode = ParameterUtil.getString(req, "functionCode");
        String registerAddress = ParameterUtil.getString(req, "registerAddress");
        String registerNumber = ParameterUtil.getString(req, "registerNumber");
        String numeralSystem = ParameterUtil.getString(req, "numeralSystem");
        // 寄存器类型，0: 线圈寄存器（1-bit)，1：保持寄存器（16-bit）
        Integer registerType = ParameterUtil.getInteger(req, "registerType");
        if (registerType == null) {
            throw new RuntimeException("寄存器类型不能为空");
        }

        // 获取寄存器地址和寄存器数量10进制值
        int registerAddressDec;
        int registerNumberDec;
        if ("DEC".equalsIgnoreCase(numeralSystem)) {
            registerAddressDec = Integer.parseInt(registerAddress);
            registerNumberDec = Integer.parseInt(registerNumber);
        } else if ("HEX".equalsIgnoreCase(numeralSystem)) {
            registerAddressDec = Integer.parseInt(registerAddress, 16);
            registerNumberDec = Integer.parseInt(registerNumber, 16);
        } else {
            throw new RuntimeException("寄存器地址进制只能为HEX或DEC");
        }
        // 存入数据库的寄存器地址
        final String registerAddressDB = registerType == 0 ? registerAddressDec + "" : registerAddress;
        // 存入数据库的寄存器类型
        final int registerTypeDB = registerType == 0 ? 0 : "HEX".equalsIgnoreCase(numeralSystem) ? 1 : 2;
        // 生成命令返回头
        final CommandResponse commandResponse = generateModBusCommandResponseHeader(slaveAddress, functionCode, registerNumberDec, registerType);

        final IsProtocolQueryCommand queryCommand;
        if (StrUtil.isBlank(queryCommandId)) {
            queryCommand = new IsProtocolQueryCommand();
        } else {
            LambdaQueryWrapper<IsProtocolQueryCommand> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(IsProtocolQueryCommand::getQueryCommandId, queryCommandId);
            queryCommand = queryCommandMapper.selectOne(wrapper);
            if (queryCommand == null) {
                throw new RuntimeException("命令不存在");
            }
        }
        queryCommand.setProtocolId(protocolId);
        queryCommand.setName(name);
        queryCommand.setCommand(command);
        queryCommand.setResponseHeader(commandResponse.getResponseHeader());
        queryCommand.setResponseLength(commandResponse.getBitLength());
        queryCommand.setGroupNumber(0);
        queryCommand.setIsDeleted(0);
        final Date now = new Date();
        if (StrUtil.isBlank(queryCommandId)) {
            queryCommand.setCrOper(req.getUinfo().getUsername());
            queryCommand.setGmtCreate(now);
        }
        queryCommand.setUpOper(req.getUinfo().getUsername());
        queryCommand.setGmtModified(now);
        queryCommand.setVersion(System.currentTimeMillis());
        queryCommand.setStartRegisterAddress(registerAddressDB);
        queryCommand.setRegisterType(registerTypeDB);
        queryCommand.setCollectCycle(collectCycle);
        if (StrUtil.isBlank(queryCommandId)) {
            queryCommand.setQueryCommandId("pqc_" + IdUtil.getIncreaseIdByNanoTime());
            queryCommandMapper.insert(queryCommand);
        } else {
            queryCommandMapper.updateById(queryCommand);
        }
        return PageData.ok();
    }

    public CommandResponse generateModBusCommandResponseHeader(String slaveAddress, int registerNumber,
            RegisterTypeEnum registerType) {
        StringBuilder responseHeader = new StringBuilder();
        // 从站地址
        responseHeader.append(slaveAddress);
        // 功能码
        responseHeader.append(registerType.getReadFunctionCode());
        // 返回数据字节数（不含header）
        int byteLength;
        // 1-bit寄存器
        if (registerType.getBitLength() == 1) {
            byteLength = registerNumber % 8 == 0 ? registerNumber / 8 : registerNumber / 8 + 1;
        } else {
            byteLength = registerNumber * 2;
        }
        responseHeader.append(StrUtil.fillBefore(Integer.toHexString(byteLength), '0', 2));
        CommandResponse commandResponse = new CommandResponse();
        commandResponse.setResponseHeader(responseHeader.toString().toUpperCase());
        int bitLength = byteLength * 8 + 16;
        commandResponse.setBitLength(bitLength);
        return commandResponse;
    }

    public String generateModBusCommand(String slaveAddress, int registerAddress, int registerNumber,
            RegisterTypeEnum registerType) {
        StringBuilder command = new StringBuilder();
        // 从站地址
        command.append(slaveAddress);
        // 功能码
        command.append(registerType.getReadFunctionCode());
        // 寄存器起始地址
        String registerAddressHex = Integer.toHexString(registerAddress);
        registerAddressHex = StrUtil.padPre(registerAddressHex, 4, '0');
        command.append(registerAddressHex);
        // 寄存器数量
        String registerNumberHex = Integer.toHexString(registerNumber);
        registerNumberHex = StrUtil.padPre(registerNumberHex, 4, '0');
        command.append(registerNumberHex);
        // CRC校验
        String crc = getCRCByTbl(command.toString().toUpperCase());
        command.append(crc);
        return command.toString().toUpperCase();
    }

    public CommandResponse generateModBusCommandResponseHeader(String slaveAddress, String functionCode,
            int registerNumber, int registerType) {
        StringBuilder responseHeader = new StringBuilder();
        // 从站地址
        responseHeader.append(slaveAddress);
        // 功能码
        responseHeader.append(functionCode);
        // 返回数据字节数（不含header）
        int byteLength;
        // 1-bit寄存器
        if (registerType == 0) {
            byteLength = registerNumber % 8 == 0 ? registerNumber / 8 : registerNumber / 8 + 1;
        } else {
            byteLength = registerNumber * 2;
        }
        responseHeader.append(StrUtil.fillBefore(Integer.toHexString(byteLength), '0', 2));
        CommandResponse commandResponse = new CommandResponse();
        commandResponse.setResponseHeader(responseHeader.toString().toUpperCase());
        int bitLength = byteLength * 8 + 16;
        commandResponse.setBitLength(bitLength);
        return commandResponse;
    }


    public String generateModBusCommand(String slaveAddress, String functionCode, String registerAddress,
            String registerNumber, String numeralSystem) {
        if (!"HEX".equalsIgnoreCase(numeralSystem) && !"DEC".equalsIgnoreCase(numeralSystem)) {
            throw new RuntimeException("寄存器地址进制只能为HEX或DEC");
        }
        StringBuilder command = new StringBuilder();
        // 从站地址
        command.append(slaveAddress);
        // 功能码
        command.append(functionCode);
        // 寄存器起始地址
        String registerAddressHex = "DEC".equalsIgnoreCase(numeralSystem) ? Integer.toHexString(Integer.parseInt(registerAddress)) : registerAddress;
        registerAddressHex = StrUtil.padPre(registerAddressHex, 4, '0').toUpperCase();
        command.append(registerAddressHex);
        // 寄存器数量
        String registerNumberHex = "DEC".equalsIgnoreCase(numeralSystem) ? Integer.toHexString(Integer.parseInt(registerNumber)) : registerNumber;
        registerNumberHex = StrUtil.padPre(registerNumberHex, 4, '0').toUpperCase();
        command.append(registerNumberHex);
        // CRC校验
        String crc = getCRCByTbl(command.toString().toUpperCase());
        command.append(crc);
        return command.toString().toUpperCase();
    }

    public PageData generateModBusCommand(DataReq req) {
        String slaveAddress = ParameterUtil.getString(req, "slaveAddress");
        String functionCode = ParameterUtil.getString(req, "functionCode");
        String registerAddress = ParameterUtil.getString(req, "registerAddress");
        String registerNumber = ParameterUtil.getString(req, "registerNumber");
        String numeralSystem = ParameterUtil.getString(req, "numeralSystem");
        String command = generateModBusCommand(slaveAddress, functionCode, registerAddress, registerNumber, numeralSystem);

        return PageData.ok("command", command);
    }

    public PageData getQueryCommands(DataReq req) {
        String protocolId = ParameterUtil.getString(req, "protocolId");
        int pageId = ParameterUtil.getInteger(req, "pageId", 1);
        int pageSize = ParameterUtil.getInteger(req, "pageSize", 20);
        if (StrUtil.isBlank(protocolId)) {
            throw new RuntimeException("协议ID不能为空");
        }
        String queryCommandId = ParameterUtil.getString(req, "queryCommandId");
        QueryWrapper<IsProtocolQueryCommand> ew = new QueryWrapper<>();
        ew.eq(IsProtocolQueryCommand.PROTOCOL_ID, protocolId)
                .eq(IsProtocolQueryCommand.IS_DELETED, 0)
                .orderByDesc(IsProtocolQueryCommand.GMT_CREATE);
        if (StrUtil.isNotBlank(queryCommandId)) {
            ew.eq(IsProtocolQueryCommand.QUERY_COMMAND_ID, queryCommandId);
        }
        IPage<Map<String, Object>> mapIPage = queryCommandMapper.selectMapsPage(new Page<>(pageId, pageSize), ew);

        List<Map<String, Object>> newResult = new ArrayList<>();
        for (Map<String, Object> map : mapIPage.getRecords()) {
            newResult.add(queryCommandConverter(map));
        }

        Page<Map<String, Object>> page = new Page<>(pageId, pageSize);
        page.setCurrent(mapIPage.getCurrent());
        page.setSize(mapIPage.getSize());
        page.setTotal(mapIPage.getTotal());
        page.setRecords(newResult);
        return PageData.ok(page);
    }

    private Map<String, Object> queryCommandConverter(Map<String, Object> queryCommandDB) {
        Map<String, Object> queryCommand = new HashMap<>();
        final String command = (String) queryCommandDB.get("command");
        final Integer registerTypeDB = (Integer) queryCommandDB.get("registerType");

        String slaveAddress = command.substring(0, 2);
        String functionCode = command.substring(2, 4);
        String registerAddressHex = command.substring(4, 8);
        String registerNumberHex = command.substring(8, 12);
        String registerAddressDec = String.valueOf(Integer.parseInt(registerAddressHex, 16));
        String registerNumberDec = String.valueOf(Integer.parseInt(registerNumberHex, 16));
        Integer registerType = registerTypeDB == 0 ? 0 : 1;
        String numeralSystem = registerTypeDB == 1 ? "HEX" : "DEC";

        queryCommand.put("queryCommandId", queryCommandDB.get("queryCommandId"));
        queryCommand.put("protocolId", queryCommandDB.get("protocolId"));
        queryCommand.put("name", queryCommandDB.get("name"));
        queryCommand.put("command", command);
        queryCommand.put("collectCycle", queryCommandDB.get("collectCycle"));
        queryCommand.put("slaveAddress", slaveAddress);
        queryCommand.put("functionCode", functionCode);
        queryCommand.put("registerType", registerType);
        queryCommand.put("numeralSystem", numeralSystem);
        queryCommand.put("registerAddress", numeralSystem.equals("DEC") ? registerAddressDec : registerAddressHex);
        queryCommand.put("registerNumber", numeralSystem.equals("DEC") ? registerNumberDec : registerNumberHex);
        return queryCommand;
    }

    private IsDataItemTemplateInfo getTemplateId(String protocolId, IsDataItemTemplateInfo lastTemplate,
            boolean isTempField) {
        Integer fieldNumber = null;
        if (null != lastTemplate) {
            final int start = isTempField ? 5 : 1;
            fieldNumber = Integer.parseInt(lastTemplate.getFieldName().substring(start));
        }
        return templateInfoMapper.getAvailableTemplateId(protocolId, fieldNumber, isTempField);
    }

    private LinkedList<IsDataItemTemplateInfo> getAvailableTemplates(String protocolId, int size) {
        final List<IsDataItemTemplateInfo> result = templateInfoMapper.getAvailableTemplates(protocolId, size);
        if (result.size() == 0) {
            throw new RuntimeException("模板数据项已用完");
        }
        return new LinkedList<>(result);
    }

    private DataType getDataType(Integer dataTypeDB, Integer convertId) {
        // 有符号
        if (dataTypeDB == 0) {
            // 十六进制转二进制 -> signed
            if (convertId == 0) {
                return DataType.SIGNED;
            }
            // 十六进制转浮点数 -> float
            else if (convertId == 8) {
                return DataType.FLOAT_AB_CD;
            }
        }
        // 无符号
        else {
            // 十六进制转十进制 -> unsigned
            if (convertId == 0) {
                return DataType.UNSIGNED;
            }
            // 十六进制转二进制 -> bit
            else if (convertId == 1) {
                return DataType.BINARY;
            }
        }
        return DataType.SIGNED;
    }

    public PageData getDataItemsByQueryCommand(DataReq req) {
        String queryCommandId = ParameterUtil.getString(req, "queryCommandId");
        String dataItemId = ParameterUtil.getString(req, "dataItemId");
        Integer startAddress = ParameterUtil.getInteger(req, "startAddress", -1);
        Integer endAddress = ParameterUtil.getInteger(req, "endAddress", -1);
        String responseData = ParameterUtil.getString(req, "responseData");

        if (StrUtil.isBlank(queryCommandId)) {
            return PageData.error("查询命令id不能为空");
        }

        IsProtocolQueryCommand command = new IsProtocolQueryCommand();
        command.setQueryCommandId(queryCommandId);
        command = queryCommandMapper.selectOne(new LambdaQueryWrapper<>(command));
        int registerCapacity = command.getRegisterType() == 0 ? 1 : 16;

        if (startAddress > -1 || endAddress > -1) {
            startAddress = startAddress > -1 ? startAddress * registerCapacity : startAddress;
            endAddress = endAddress > -1 ? endAddress * registerCapacity : endAddress;
        }
        QueryWrapper<IsDeviceProtocolDataItemInfo> ew = new QueryWrapper<>();
        ew.eq(IsDeviceProtocolDataItemInfo.IS_DELETED, 0)
                .eq(IsDeviceProtocolDataItemInfo.QUERY_COMMAND_ID, queryCommandId)
                .eq(StrUtil.isNotBlank(dataItemId), IsDeviceProtocolDataItemInfo.DATA_ITEM_ID, dataItemId)
                .ge(startAddress > -1, IsDeviceProtocolDataItemInfo.START_BYTE, startAddress)
                .le(endAddress > -1, IsDeviceProtocolDataItemInfo.START_BYTE, endAddress)
                .orderByDesc(IsDeviceProtocolDataItemInfo.START_BYTE);
        Page<IsDeviceProtocolDataItemInfo> page = (Page<IsDeviceProtocolDataItemInfo>) dataItemInfoMapper.selectPage(new Page<>(req.getPageid(), req.getPagesize()), ew);
        final List<Map<String, Object>> result = new ArrayList<>();
        // 查询数据项详情
        for (IsDeviceProtocolDataItemInfo dataItem : page.getRecords()) {
            Map<String, Object> map = new HashMap<>();
            map.put("dataItemId", dataItem.getDataItemId());
            map.put("protocolId", dataItem.getProtocolId());
            map.put("queryCommandId", dataItem.getQueryCommandId());
            map.put("name", dataItem.getName());
            map.put("unit", dataItem.getUnitValue());
            map.put("unitName", dataItem.getUnitValue());
            map.put("offset", dataItem.getStartByte());
            final DataType dataType = getDataType(dataItem.getDataType(), dataItem.getConvertId());
            map.put("dataType", dataType.getIndex());
            map.put("dataTypeName", dataType.getName());
            map.put("formula", dataItem.getFormula());
            map.put("upperLimit", dataItem.getReasonableRangeUp());
            map.put("lowerLimit", dataItem.getReasonableRangeLow());
            map.put("writable", false);


            QueryWrapper<IsDataItemField> ewField = new QueryWrapper<>();
            ewField.eq(IsDataItemField.IS_DELETED, 0)
                    .eq(IsDataItemField.DATA_ITEM_ID, dataItem.getDataItemId())
                    .orderByDesc(IsDataItemField.VALUE);
            final List<IsDataItemField> fields = dataItemFieldMapper.selectList(ewField);
            StringBuilder parseRule = new StringBuilder();
            for (IsDataItemField field : fields) {
                parseRule.append(field.getValue()).append("-").append(field.getName()).append(";");
            }
            parseRule.deleteCharAt(parseRule.length() - 1);
            map.put("parseRule", parseRule.toString());
            // 解析数据
            if (StrUtil.isNotBlank(responseData)) {
                final String source = responseData.substring(dataItem.getStartByte() / 4, dataItem.getStartByte() / 4 + 4);
                map.put("parsedValue", parseData(dataType.getIndex(), source));
            }
            result.add(map);
        }

        return PageData.ok(page, result);
    }

    public PageData parseData(DataReq req) {
        String queryCommandId = ParameterUtil.getString(req, "queryCommandId");
        String commandResponse = ParameterUtil.getString(req, "commandResponse");
        if (StrUtil.isBlank(queryCommandId)) {
            return PageData.error("查询命令id不能为空");
        }
        if (StrUtil.isBlank(commandResponse)) {
            return PageData.error("命令响应不能为空");
        }
        List<IsProtocolQueryCommand> commands = queryCommandMapper.selectList(new QueryWrapper<IsProtocolQueryCommand>().eq(IsProtocolQueryCommand.IS_DELETED, 0)
                .eq(IsProtocolQueryCommand.QUERY_COMMAND_ID, queryCommandId));
        if (commands.size() == 0) {
            return PageData.error("查询命令不存在");
        }
        IsProtocolQueryCommand command = commands.get(0);
        String responseHeader = command.getResponseHeader();
        int registerCapacity = command.getRegisterType() == 0 ? 1 : 16;
        Integer startRegisterAddress = Integer.valueOf(command.getStartRegisterAddress());

        List<IsDeviceProtocolDataItemInfo> dataItems = dataItemInfoMapper.selectList(new QueryWrapper<IsDeviceProtocolDataItemInfo>().eq(IsDeviceProtocolDataItemInfo.IS_DELETED, 0)
                .eq(IsDeviceProtocolDataItemInfo.QUERY_COMMAND_ID, queryCommandId)
                .orderByDesc(IsDeviceProtocolDataItemInfo.START_BYTE)
                .orderByDesc(IsDeviceProtocolDataItemInfo.BIT_PLACE));
        // 解析数据
        List<Map<String, Object>> result = new ArrayList<>();
        // 去头
        if (commandResponse.startsWith(responseHeader)) {
            commandResponse = commandResponse.substring(responseHeader.length());
        }
        // 十六进制转bit
        if (registerCapacity == 1) {
            commandResponse = HexUtils.HexByte2Bit(commandResponse);
        }
        for (IsDeviceProtocolDataItemInfo dataItem : dataItems) {
            final DataType dataType = DataType.valueOf(dataItem.getDataTypeEnum());
            final int offset = dataItem.getRegisterAddress() - startRegisterAddress;
            final int length = dataItem.getByteLength();
            String parsedVal;
            String sourceVal;
            // 十六位寄存器
            if (registerCapacity > 1 && dataType.getLength() > 1) {
                sourceVal = commandResponse.substring(offset * 4, (offset * 4) + (length / 4));
                if (dataItem.getEndian() == 1) {
                    // 大端
                    sourceVal = sourceVal.substring(sourceVal.length() / 2) + sourceVal.substring(0, sourceVal.length() / 2);
                }
                // 解析
                parsedVal = parseData(dataType, sourceVal);
            }
            // 十六位截取单个bit位
            else if (registerCapacity > 1 && dataType.getLength() == 1) {
                sourceVal = commandResponse.substring(offset * 4, offset * 4 + 4);
                // bit顺序，15-0
                final String aByte = HexUtils.HexByte2Bit(sourceVal);
                //                int bitOffset = 15 - dataItem.getBitPlace();
                // modify by zhouy
                int bitOffset = 15 - dataItem.getBitPlace();
                parsedVal = parseData(dataType, aByte.substring(bitOffset, bitOffset + 1));
            }
            // 1位寄存器
            else {
                int startBit = offset / 8 * 8;
                final String aByte = commandResponse.substring(startBit, startBit + 8);
                // bit顺序15-0
                parsedVal = parseData(dataType, aByte.substring(7 - offset % 8, 7 - offset % 8 + 1));
                sourceVal = parsedVal;
            }
            String parsedValName = parsedVal;
            String formula = dataItem.getFormula();
            if (StrUtil.isNotBlank(formula)) {
                formula = formula.replace("x", parsedValName);
                try {
                    final Object eval = JavaScriptEngine.instance().eval(formula);
                    if (eval instanceof Double || eval instanceof Integer) {
                        parsedValName = String.valueOf(eval);
                    }
                } catch (ScriptException e) {
                    e.printStackTrace();
                }
            }
            // 枚举值
            if (NumberUtil.isInteger(parsedVal)) {
                final String intVal = String.valueOf(Integer.parseInt(parsedVal));
                final List<IsDataItemField> fields = dataItemFieldMapper.selectList(new QueryWrapper<IsDataItemField>().eq(IsDataItemField.IS_DELETED, 0)
                        .eq(IsDataItemField.DATA_ITEM_ID, dataItem.getDataItemId()));
                for (IsDataItemField field : fields) {
                    if (field.getValue().equals(intVal)) {
                        parsedValName = field.getName();
                        break;
                    }
                }
            }
            Map<String, Object> map = new HashMap<>();
            map.put("dataName", dataItem.getName());
            map.put("sourceVal", sourceVal);
            map.put("parsedVal", parsedVal);
            map.put("parsedValName", parsedValName);
            result.add(map);
        }
        return PageData.ok(result);
    }

    private String parseData(DataType dataType, String source) {
        String value = null;
        if (dataType == DataType.SIGNED) {
            int intVal = Integer.parseInt(source, 16);
            if (intVal > 32767) {
                intVal = intVal - 65536;
            }
            value = String.valueOf(intVal);
        } else if (dataType == DataType.UNSIGNED) {
            value = String.valueOf(Integer.parseInt(source, 16));
        } else if (dataType == DataType.FLOAT_AB_CD || dataType == DataType.FLOAT_CD_AB) {
            // 十六进制转浮点数
            int intVal = Integer.parseInt(source, 16);
            value = String.valueOf(Float.intBitsToFloat(intVal));
        } else if (dataType == DataType.BINARY) {
            // 十六进制转二进制
            int intVal = Integer.parseInt(source, 16);
            value = Integer.toBinaryString(intVal);
        } else if (dataType == DataType.SIGNED_LONG_AB_CD || dataType == DataType.SIGNED_LONG_CD_AB) {
            // 十六进制转长整型
            long longVal = Long.parseLong(source, 16);
            if (longVal > 2147483647) {
                longVal = longVal - 4294967296L;
            }
            value = String.valueOf(longVal);
        } else if (dataType == DataType.UNSIGNED_LONG_AB_CD || dataType == DataType.UNSIGNED_LONG_CD_AB) {
            // 十六进制转无符号长整型
            long longVal = Long.parseLong(source, 16);
            value = String.valueOf(longVal);
        } else if (dataType == DataType.ASCII) {
            // 十六进制转ASCII码
            value = HexUtil.decodeHexStr(source);
        } else if (dataType == DataType.HEX) {
            // 十六进制转十六进制
            value = source;
        }
        return value;
    }

    private Number parseData(int dataType, String source) {
        source = StrUtil.padPre(source, 4, '0');
        Number value = null;
        if (dataType == DataType.SIGNED.getIndex()) {
            int intVal = Integer.parseInt(source, 16);
            if (intVal > 32767) {
                intVal = intVal - 65536;
            }
            value = intVal;
        } else if (dataType == DataType.UNSIGNED.getIndex()) {
            value = Integer.parseInt(source, 16);
        } else if (dataType == DataType.FLOAT_AB_CD.getIndex()) {
            // 十六进制转浮点数
            int intVal = Integer.parseInt(source, 16);
            value = Float.intBitsToFloat(intVal);
        }
        return value;
    }

    public IsProtocolQueryCommand generateQueryCommand(int startAddress, int endAddress, int registerCount,
            RegisterTypeEnum registerType, String protocolId, String userId, String slaveAddress) {
        final Date now = new Date();
        final CommandResponse response = generateModBusCommandResponseHeader(slaveAddress, registerCount, registerType);
        final String command = generateModBusCommand(slaveAddress, startAddress, registerCount, registerType);
        final IsProtocolQueryCommand queryCommand = new IsProtocolQueryCommand();
        queryCommand.setQueryCommandId("pqc_" + IdUtil.getIncreaseIdByNanoTime());
        queryCommand.setProtocolId(protocolId);
        queryCommand.setName(registerType.getReadFunctionCode() + "查询指令" + startAddress + "-" + endAddress);
        queryCommand.setResponseHeader(response.responseHeader);
        queryCommand.setResponseLength(response.bitLength);
        queryCommand.setCommand(command);
        queryCommand.setCrOper(userId);
        queryCommand.setGmtCreate(now);
        queryCommand.setUpOper(userId);
        queryCommand.setGmtModified(now);
        queryCommand.setStartRegisterAddress(String.valueOf(startAddress));
        queryCommand.setRegisterType(registerType.getBitLength() == 1 ? 0 : 2);
        queryCommand.setCollectCycle(300);
        return queryCommand;
    }

    public PageData getDataItems(DataReq req) {
        Integer pageId = ParameterUtil.getInteger(req, "pageId", 1);
        Integer pageSize = ParameterUtil.getInteger(req, "pageSize", 1000);
        String protocolId = ParameterUtil.getString(req, "protocolId");
        Integer startRegisterAddress = ParameterUtil.getInteger(req, "startRegisterAddress");
        Integer endRegisterAddress = ParameterUtil.getInteger(req, "endRegisterAddress");
        Integer registerAddress = ParameterUtil.getInteger(req, "registerAddress");
        String dataName = ParameterUtil.getString(req, "dataName");
        String registerType = ParameterUtil.getString(req, "registerType");
        Page<IsDeviceProtocolDataItemInfo> page = new Page<>(pageId, pageSize);
        final List<IsDeviceProtocolDataItemInfo> dataItemInfoList = dataItemInfoMapper.getDataItems(page, protocolId, startRegisterAddress, endRegisterAddress, registerAddress, dataName, registerType);
        List<RegisterDataItem> dataItems = new ArrayList<>();
        for (IsDeviceProtocolDataItemInfo dataItemInfo : dataItemInfoList) {
            final RegisterDataItem registerDataItem = new RegisterDataItem(dataItemInfo);
            dataItems.add(registerDataItem);
        }
        if (CollUtil.isNotEmpty(dataItems)) {
            final List<String> dataItemIds = new ArrayList<>();
            for (RegisterDataItem dataItem : dataItems) {
                dataItemIds.add(dataItem.getDataItemId());
            }
            // 数据项详情
            final List<IsDataItemField> dataItemFields = dataItemFieldMapper.selectList(new QueryWrapper<IsDataItemField>().in(IsDataItemField.DATA_ITEM_ID, dataItemIds)
                    .eq(IsDataItemField.IS_DELETED, 0)
                    .orderByDesc(IsDataItemField.DATA_ITEM_ID)
                    .orderByDesc(IsDataItemField.VALUE));
            if (CollUtil.isNotEmpty(dataItemFields)) {
                for (IsDataItemField dataItemField : dataItemFields) {
                    for (RegisterDataItem dataItem : dataItems) {
                        if (dataItem.getDataItemId().equals(dataItemField.getDataItemId())) {
                            String value = dataItemField.getValue();
                            String name = dataItemField.getName();
                            String dataEnum = value + ":" + name;
                            dataItem.setDataEnum(StrUtil.isBlank(dataItem.getDataEnum()) ? dataEnum : dataItem.getDataEnum() + ";" + dataEnum);
                            break;
                        }
                    }
                }
            }

            // 控制指令
            final List<IsProtocolControlCommand> controlCommands = controlCommandMapper.selectList(new QueryWrapper<IsProtocolControlCommand>().in(IsProtocolControlCommand.RELATION_DATA_ITEM_IDS, dataItemIds));
            if (CollUtil.isNotEmpty(controlCommands)) {
                for (RegisterDataItem dataItem : dataItems) {
                    dataItem.setWritable(false);
                    for (IsProtocolControlCommand controlCommand : controlCommands) {
                        if (dataItem.getDataItemId().equals(controlCommand.getRelationDataItemIds())) {
                            dataItem.setWritable(true);
                            dataItem.setControlFormula(controlCommand.getFormula());
                            // 控制指令参数
                            final List<IsEquipmentFunctionDetailInfo> functionDetailInfos = functionDetailInfoMapper.selectList(new QueryWrapper<IsEquipmentFunctionDetailInfo>().eq(IsEquipmentFunctionDetailInfo.CONTROL_COMMAND_ID, controlCommand.getControlCommandId())
                                    .ge(IsEquipmentFunctionDetailInfo.VALUE, -65536)
                                    .orderByDesc(IsEquipmentFunctionDetailInfo.VALUE));
                            if (CollUtil.isNotEmpty(functionDetailInfos)) {
                                StringBuilder sb = new StringBuilder();
                                for (IsEquipmentFunctionDetailInfo functionDetailInfo : functionDetailInfos) {
                                    sb.append(functionDetailInfo.getValue().setScale(0, RoundingMode.DOWN))
                                            .append(":")
                                            .append(functionDetailInfo.getName());
                                    if (StrUtil.isNotBlank(functionDetailInfo.getRelateValue())) {
                                        sb.append(":").append(functionDetailInfo.getRelateValue());
                                    }
                                    sb.append(";");
                                }
                                if (sb.charAt(sb.length() - 1) == ';') {
                                    sb.deleteCharAt(sb.length() - 1);
                                }
                                dataItem.setControlParamEnum(sb.toString());
                            }
                            break;
                        }
                    }
                }
            }
        }
        return PageData.ok(page, dataItems);
    }

    @OpeTransactional
    public PageData importProtocol(String manufactureId, String username, String protocolId,
            LinkedList<RegisterDataItem> inputs) {
        if (CollUtil.isEmpty(inputs)) {
            return PageData.error("导入数据为空");
        }

        // add by zhouy （清空协议查询和控制指令）
        emptyProtocol(protocolId);

        // 待删除的数据项
        final List<String> dataItemIds = new ArrayList<>();
        // 查询原始数据，用于比对
        List<IsDeviceProtocolDataItemInfo> dataItemInfos;
        int page = 0;
        while (true) {
            dataItemInfos = dataItemInfoMapper.selectList(new QueryWrapper<IsDeviceProtocolDataItemInfo>().select(IsDeviceProtocolDataItemInfo.DATA_ITEM_ID, IsDeviceProtocolDataItemInfo.NAME, IsDeviceProtocolDataItemInfo.REGISTER_ADDRESS, IsDeviceProtocolDataItemInfo.REGISTER_TYPE, IsDeviceProtocolDataItemInfo.BIT_PLACE)
                    .eq(IsDeviceProtocolDataItemInfo.PROTOCOL_ID, protocolId)
                    .eq(IsDeviceProtocolDataItemInfo.IS_DELETED, 0)
                    .last("limit " + page * 1000 + ", 1000"));
            if (CollUtil.isEmpty(dataItemInfos)) {
                break;
            }
            // 比对地址和类型,如果相同则赋值dataItemId
            ListIterator<IsDeviceProtocolDataItemInfo> it = dataItemInfos.listIterator();
            while (it.hasNext()) {
                final IsDeviceProtocolDataItemInfo dataItemInfo = it.next();
                for (RegisterDataItem registerDataItem : inputs) {
                    if (dataItemInfo.getRegisterAddress()
                            .equals(registerDataItem.getRegisterAddress()) && dataItemInfo.getRegisterType()
                            .equals(registerDataItem.getRegisterType()) && Objects.equals(dataItemInfo.getBitPlace(), registerDataItem.getBitAddress())) {
                        registerDataItem.setDataItemId(dataItemInfo.getDataItemId());
                        it.remove();
                        break;
                    }
                }
            }
            it = dataItemInfos.listIterator();
            // dataItemId依然为空，比对名称和类型,如果相同则赋值dataItemId
            while (it.hasNext()) {
                final IsDeviceProtocolDataItemInfo dataItemInfo = it.next();
                for (RegisterDataItem registerDataItem : inputs) {
                    if (StrUtil.isBlank(registerDataItem.getDataItemId()) && dataItemInfo.getName()
                            .equals(registerDataItem.getDataName()) && dataItemInfo.getRegisterType()
                            .equals(registerDataItem.getRegisterType()) && Objects.equals(dataItemInfo.getBitPlace(), registerDataItem.getBitAddress())) {
                        registerDataItem.setDataItemId(dataItemInfo.getDataItemId());
                        it.remove();
                        break;
                    }
                }
            }
            // 删除不存在的数据项
            for (IsDeviceProtocolDataItemInfo dataItemInfo : dataItemInfos) {
                dataItemIds.add(dataItemInfo.getDataItemId());
            }
            dataItemInfos.clear();
            page++;
        }
        // 删除已移除的数据项和数据项详情
        if (CollUtil.isNotEmpty(dataItemIds)) {
            dataItemInfoMapper.delete(new QueryWrapper<IsDeviceProtocolDataItemInfo>().in(IsDeviceProtocolDataItemInfo.DATA_ITEM_ID, dataItemIds));
            dataItemFieldMapper.delete(new QueryWrapper<IsDataItemField>().in(IsDataItemField.DATA_ITEM_ID, dataItemIds));
            functionDetailInfoMapper.deleteByDataItemIds(dataItemIds);
            controlCommandMapper.delete(new QueryWrapper<IsProtocolControlCommand>().in(IsProtocolControlCommand.RELATION_DATA_ITEM_IDS, dataItemIds));
        }

        saveDataItems(manufactureId, username, protocolId, inputs);
        return PageData.ok();
    }

    /**
     * 清空协议
     * add by zhouy
     * @param protocolId
     */
    private void emptyProtocol(String protocolId) {

        controlCommandMapper.delete(new QueryWrapper<IsProtocolControlCommand>().eq(IsProtocolControlCommand.PROTOCOL_ID, protocolId));

        queryCommandMapper.delete(new QueryWrapper<IsProtocolQueryCommand>().eq(IsProtocolQueryCommand.PROTOCOL_ID, protocolId));

        functionDetailInfoMapper.delete(new QueryWrapper<IsEquipmentFunctionDetailInfo>().eq(IsEquipmentFunctionDetailInfo.PROTOCOL_ID, protocolId));

        dataItemInfoMapper.delete(new QueryWrapper<IsDeviceProtocolDataItemInfo>().eq(IsDeviceProtocolDataItemInfo.PROTOCOL_ID, protocolId));

        standardDataItemMapper.delete(new QueryWrapper<AStandardDataItem>().eq(AStandardDataItem.PROTOCOL_ID, protocolId));

        equipmentControlParamMapper.delete(new QueryWrapper<EquipmentControlParam>().eq(AStandardDataItem.PROTOCOL_ID, protocolId));

    }

    @OpeTransactional
    public PageData deleteDataItem(DataReq req) {

        String dataItemId = ParameterUtil.getString(req, "dataItemId");
        if (StrUtil.isBlank(dataItemId)) {
            return PageData.ok();
        }

        // add by zhouy(删除查询指令)
        //final List<NesProtocolDataSet> inputList = new ArrayList<>();
        IsDeviceProtocolDataItemInfo dataItemInfo = dataItemInfoMapper.selectOne(new LambdaQueryWrapper<IsDeviceProtocolDataItemInfo>().eq(IsDeviceProtocolDataItemInfo::getDataItemId, dataItemId));
        if (dataItemInfo != null) {

            dataItemInfoMapper.delete(new QueryWrapper<IsDeviceProtocolDataItemInfo>().eq(IsDeviceProtocolDataItemInfo.DATA_ITEM_ID, dataItemId));

            dataItemFieldMapper.delete(new QueryWrapper<IsDataItemField>().eq(IsDataItemField.DATA_ITEM_ID, dataItemId));

            // 删除查询指令(如果查询指令没有其他的数据项的话)
            List<IsDeviceProtocolDataItemInfo> dataItemInfos = dataItemInfoMapper.selectList(new QueryWrapper<IsDeviceProtocolDataItemInfo>().select(IsDeviceProtocolDataItemInfo.ID, IsDeviceProtocolDataItemInfo.REGISTER_ADDRESS, IsDeviceProtocolDataItemInfo.REGISTER_TYPE, IsDeviceProtocolDataItemInfo.BYTE_LENGTH, IsDeviceProtocolDataItemInfo.DATA_TYPE_ENUM, IsDeviceProtocolDataItemInfo.BIT_PLACE, IsDeviceProtocolDataItemInfo.IS_READ_WRITE)
                    .eq(IsDeviceProtocolDataItemInfo.PROTOCOL_ID, dataItemInfo.getProtocolId())
                    .eq(IsDeviceProtocolDataItemInfo.QUERY_COMMAND_ID, dataItemInfo.getQueryCommandId())
                    .eq(IsDeviceProtocolDataItemInfo.IS_DELETED, 0));

            if (dataItemInfos.size() == 0) {
                NesProtocolDataSet dataSet = new NesProtocolDataSet();
                dataSet.setDataItemInfo(dataItemInfo);
                RegisterDataItem registerDataItem = new RegisterDataItem();
                registerDataItem.setRegisterAddress(dataItemInfo.getRegisterAddress());
                registerDataItem.setRegisterType(dataItemInfo.getRegisterType());
                registerDataItem.setDataType(dataItemInfo.getDataTypeEnum());
                if (ObjUtil.isEmpty(dataItemInfo.getBitPlaceEnd())) {
                    registerDataItem.setBitAddress(String.valueOf(dataItemInfo.getBitPlace()));
                } else {
                    registerDataItem.setBitAddress(String.format("%s-%s", dataItemInfo.getBitPlace(), dataItemInfo.getBitPlaceEnd()));
                }

                dataSet.setRegisterDataItem(registerDataItem);
                // inputList.add(dataSet);
                List<IsProtocolQueryCommand> queryCommandList = queryCommandMapper.getQueryCommand(dataItemInfo.getProtocolId(), null);
                new ModbusProtocolUtil().deleteQueryCommand(dataSet, queryCommandList, queryCommandMapper);
            }
        }


        final List<IsProtocolControlCommand> controlCommand = controlCommandMapper.selectList(new QueryWrapper<IsProtocolControlCommand>().eq(IsProtocolControlCommand.RELATION_DATA_ITEM_IDS, dataItemId)
                .eq(IsProtocolControlCommand.IS_DELETED, 0));

        if (CollUtil.isNotEmpty(controlCommand)) {
            controlCommandMapper.deleteById(controlCommand.get(0).getId());
            functionDetailInfoMapper.delete(new QueryWrapper<IsEquipmentFunctionDetailInfo>().eq(IsEquipmentFunctionDetailInfo.CONTROL_COMMAND_ID, controlCommand.get(0)
                    .getControlCommandId()));
        }
        return PageData.ok();
    }

    public PageData getProtocolQueryCommand(DataReq req) {
        Integer pageId = ParameterUtil.getInteger(req, "pageId", 1);
        Integer pageSize = ParameterUtil.getInteger(req, "pageSize", 100);
        String protocolId = ParameterUtil.getString(req, "protocolId");
        // 寄存器类型
        String registerType = ParameterUtil.getString(req, "registerType");
        // 寄存器地址
        Integer registerAddress = ParameterUtil.getInteger(req, "registerAddress");
        // 寄存器起始地址
        Integer startRegisterAddress = ParameterUtil.getInteger(req, "startRegisterAddress");
        // 寄存器结束地址
        Integer endRegisterAddress = ParameterUtil.getInteger(req, "endRegisterAddress");
        if (StrUtil.isBlank(protocolId)) {
            return PageData.error("协议id不能为空");
        }
        Page<Map<String, Object>> page = new Page<>(pageId, pageSize);
        List<Map<String, Object>> result = queryCommandMapper.getProtocolQueryCommand(page, protocolId, registerType, registerAddress, startRegisterAddress, endRegisterAddress);
        return PageData.ok(page, result);
    }

    @OpeTransactional
    public PageData saveRegisterDataItems(DataReq req) {
        String username = req.getUinfo().getUsername();
        JSONArray list = ParameterUtil.getJSONArray(req, "params");
        String protocolId = ParameterUtil.getString(req, "protocolId");
        String manufactureId = getProtocolManufactureId(protocolId, req);
        final LinkedList<RegisterDataItem> registerDataItems = new LinkedList<>(list.toJavaList(RegisterDataItem.class));
        // modify by zhouy
        registerDataItems.forEach(registerDataItem -> {
            String str = registerDataItem.getOrgBitAddress();
            if (StrUtil.isBlank(str)) {
                registerDataItem.setBitPlace(null);
                registerDataItem.setBitPlaceEnd(null);
            } else {
                if (str.contains("-")) {
                    registerDataItem.setBitPlace(Integer.valueOf(str.split("-")[0]));
                    registerDataItem.setBitPlaceEnd(Integer.valueOf(str.split("-")[1]));
                } else {
                    registerDataItem.setBitPlace(Integer.valueOf(str));
                    registerDataItem.setBitPlaceEnd(null);
                }
            }
        });
        // 比对下数据，如果起存器地址、bit位、寄存器有变化, 重新生成查询指令
        saveDataItems(manufactureId, username, protocolId, registerDataItems);

        // modify by zhouy
        // 修改协议，自动重新生成控制参数
        generateEquipmentControlParam(protocolId);

        return PageData.ok();
    }

    public void saveDataItems(String manufactureId, String username, String protocolId,
            LinkedList<RegisterDataItem> registerDataItems) {
        // 版本号
        Date now = new Date();
        // 获取数据库字段绑定关系
        final Map<String, String> registerTemplateMap = getRegisterTemplateMap(protocolId, registerDataItems);
        // 获取分组数据
        Map<String, String> classifyMap = getParamClassifies(manufactureId, registerDataItems);

        // 获取协议从站地址
        final IsDeviceProtocolInfo protocol = deviceProtocolInfoMapper.selectList(new QueryWrapper<IsDeviceProtocolInfo>().eq(IsDeviceProtocolInfo.PROTOCOL_ID, protocolId)
                .eq(IsDeviceProtocolInfo.IS_DELETED, 0)).get(0);
        String slaveAddress = protocol.getSlaveAddress();
        Integer max16BitRegisterReadCount = protocol.getMax16BitRegisterReadCount();
        Integer max1BitRegisterReadCount = protocol.getMax1BitRegisterReadCount();
        if (StrUtil.isBlank(slaveAddress)) {
            slaveAddress = "01";
        }
        ListIterator<RegisterDataItem> iterator = registerDataItems.listIterator();
        // 删除空数据，处理数据格式
        while (iterator.hasNext()) {
            RegisterDataItem registerDataItem = iterator.next();
            if (StrUtil.isBlank(registerDataItem.getDataName()) || registerDataItem.getRegisterAddress() == null || StrUtil.isBlank(registerDataItem.getRegisterType()) || StrUtil.isBlank(registerDataItem.getDataType())) {
                iterator.remove();
                continue;
            }

            registerDataItem.setManufactureId(manufactureId);
            registerDataItem.setProtocolId(protocolId);

            registerDataItem.setDataName(registerDataItem.getDataName().trim());
            registerDataItem.setRegisterType(registerDataItem.getRegisterType().trim());
            registerDataItem.setDataUnit(registerDataItem.getDataUnit() == null ? null : registerDataItem.getDataUnit()
                    .trim());
            registerDataItem.setDataType(registerDataItem.getDataType() == null ? null : registerDataItem.getDataType()
                    .trim());
            registerDataItem.setFormula(registerDataItem.getFormula() == null ? null : registerDataItem.getFormula()
                    .trim());
            registerDataItem.setFaultNormalValue(registerDataItem.getFaultNormalValue() == null ? null : registerDataItem.getFaultNormalValue()
                    .trim());
            registerDataItem.setControlFormula(registerDataItem.getControlFormula() == null ? null : registerDataItem.getControlFormula()
                    .trim());
        }
        if (registerDataItems.isEmpty()) {
            return;
        }
        final List<NesProtocolDataSet> inputList = new ArrayList<>();
        // 转换格式
        for (RegisterDataItem registerDataItem : registerDataItems) {
            final NesProtocolDataSet tmp = registerDataItem.convertToNesFormat(username, slaveAddress);
            final StringBuilder key = new StringBuilder(registerDataItem.getRegisterType()).append("_")
                    .append(registerDataItem.getRegisterAddress());
            if (registerDataItem.getBitAddress() != null) {
                key.append("_").append(registerDataItem.getBitAddress());
            }
            // add by zhouy
            if (registerDataItem.getReadable()) {
                tmp.getDataItemInfo().setIsReadWrite(2);
            } else {
                tmp.getDataItemInfo().setIsReadWrite(3);
            }

            tmp.getDataItemInfo().setTemplateId(registerTemplateMap.get(key.toString()));
            tmp.getDataItemInfo().setClassify(classifyMap.getOrDefault(tmp.getDataItemInfo().getClassify(), ""));
            inputList.add(tmp);
        }
        Collections.sort(inputList);

        // 生成查询指令
        new ModbusProtocolUtil().generateQueryCommand(inputList, protocolId, username, slaveAddress, max1BitRegisterReadCount, max16BitRegisterReadCount, this, queryCommandMapper);

        checkExistsDataField(inputList, protocolId);
        checkExistsControlCommand(inputList, protocolId);
        checkExistsControlCommandDetail(inputList, protocolId);


        // 新增
        Set<IsProtocolQueryCommand> insertQueryCommands = new HashSet<>();
        Set<IsDeviceProtocolDataItemInfo> insertDataItems = new HashSet<>();
        Set<IsDataItemField> insertDataItemFields = new HashSet<>();
        Set<IsProtocolControlCommand> insertControlCommands = new HashSet<>();
        Set<IsEquipmentFunctionDetailInfo> insertControlParams = new HashSet<>();
        // 更新
        Set<IsProtocolQueryCommand> updateQueryCommands = new HashSet<>();
        Set<IsDeviceProtocolDataItemInfo> updateDataItems = new HashSet<>();
        Set<IsDataItemField> updateDataItemFields = new HashSet<>();
        Set<IsProtocolControlCommand> updateControlCommands = new HashSet<>();
        Set<IsEquipmentFunctionDetailInfo> updateControlParams = new HashSet<>();

        // 查询已存在的反控项数据项
        List<IsDeviceProtocolDataItemInfo> existsBitControlDataItems = dataItemInfoMapper.selectList(new QueryWrapper<IsDeviceProtocolDataItemInfo>().eq(IsDeviceProtocolDataItemInfo.IS_DELETED, 0)
                .eq(IsDeviceProtocolDataItemInfo.PROTOCOL_ID, protocolId)
                .likeRight(IsDeviceProtocolDataItemInfo.NAME, "bit反控"));

        // bit反控项数据项
        HashMap<Integer, IsDeviceProtocolDataItemInfo> bitControlDataItemMap = new HashMap<>();
        for (IsDeviceProtocolDataItemInfo bitControlDataItem : existsBitControlDataItems) {
            // 标记一下，不新增
            bitControlDataItem.setIsDeleted(1);
            bitControlDataItemMap.put(bitControlDataItem.getRegisterAddress(), bitControlDataItem);
        }

        IsDataItemTemplateInfo lastBitControlTemplate = null;

        // 保存数据项和数据项详情
        for (NesProtocolDataSet dataSet : inputList) {
            final IsDeviceProtocolDataItemInfo dataItemInfo = dataSet.getDataItemInfo();
            final RegisterDataItem registerDataItem = dataSet.getRegisterDataItem();
            // 查询指令
            final IsProtocolQueryCommand queryCommand = dataSet.getQueryCommand();
            if (queryCommand != null) {
                insertQueryCommands.add(queryCommand);
            }

            String dataItemId = dataItemInfo.getDataItemId();
            // 数据项
            if (StrUtil.isNotBlank(dataItemId)) {
                if (dataItemInfo.getStartByte() != null && dataItemInfo.getStartByte() < 0) {
                    dataItemInfo.setStartByte(null);
                }
                dataItemInfoMapper.update(dataItemInfo, new QueryWrapper<IsDeviceProtocolDataItemInfo>().eq(IsDeviceProtocolDataItemInfo.DATA_ITEM_ID, dataItemId));
            } else {
                dataItemId = "dpdi_" + IdUtil.getIncreaseIdByNanoTime();
                dataItemInfo.setDataItemId(dataItemId);
                dataItemInfo.setCrOper(username);
                dataItemInfo.setGmtCreate(now);
                dataItemInfo.setUpOper(username);
                dataItemInfo.setGmtModified(now);
                dataItemInfo.setIsDeleted(0);
                insertDataItems.add(dataItemInfo);
            }
            // 生成bit反控项数据项
            final RegisterTypeEnum registerType = RegisterTypeEnum.valueOf(registerDataItem.getRegisterType());

            // modify by zhouy
            // 生成临时数据项，按位操作时，提供反控查询。
            // && dataItemInfo.getBitPlace() > -1
            if (dataItemInfo.getBitPlace() != null && Boolean.TRUE.equals(registerDataItem.getWritable()) && registerType.getBitLength() > 1) {
                lastBitControlTemplate = generateBitControlDataItem(dataItemInfo, bitControlDataItemMap, lastBitControlTemplate);
            }

            // 数据项详情
            final List<IsDataItemField> dataItemFields = dataSet.getDataItemField();
            if (CollUtil.isNotEmpty(dataItemFields)) {
                for (IsDataItemField dataItemField : dataItemFields) {
                    dataItemField.setDataItemId(dataItemId);
                    dataItemField.setProtocolQueryCommandId(dataItemInfo.getQueryCommandId());
                    if (dataItemField.getId() == null) {
                        insertDataItemFields.add(dataItemField);
                    } else {
                        updateDataItemFields.add(dataItemField);
                    }
                }
            }
            // 保存控制指令
            final IsProtocolControlCommand controlCommand = dataSet.getControlCommand();
            if (controlCommand != null) {
                controlCommand.setRelationDataItemIds(dataItemId);
                if (controlCommand.getId() == null) {
                    insertControlCommands.add(controlCommand);
                } else {
                    updateControlCommands.add(controlCommand);
                }
                // bit反控项数据项
                if (controlCommand.getControlBitPlace() != null && bitControlDataItemMap.containsKey(dataItemInfo.getRegisterAddress())) {
                    controlCommand.setControlData(bitControlDataItemMap.get(dataItemInfo.getRegisterAddress())
                            .getDataItemId());
                }
                // 控制指令参数详情
                if (CollUtil.isNotEmpty(dataSet.getControlCommandParams())) {
                    for (IsEquipmentFunctionDetailInfo controlParam : dataSet.getControlCommandParams()) {
                        if (controlParam.getId() == null) {
                            insertControlParams.add(controlParam);
                        } else {
                            updateControlParams.add(controlParam);
                        }
                    }
                }
            }
        }
        // 批量插入
        if (CollUtil.isNotEmpty(insertQueryCommands)) {
            for (IsProtocolQueryCommand insertQueryCommand : insertQueryCommands) {
                insertQueryCommand.setVersion(now.getTime());
            }
            queryCommandMapper.insertBatch(insertQueryCommands);
        }
        if (CollUtil.isNotEmpty(insertDataItems)) {
            for (IsDeviceProtocolDataItemInfo insertDataItem : insertDataItems) {
                insertDataItem.setVersion(now.getTime());
            }
            dataItemInfoMapper.insertBatch(insertDataItems);
        }
        if (CollUtil.isNotEmpty(updateDataItems)) {
            for (IsDeviceProtocolDataItemInfo updateDataItem : updateDataItems) {
                updateDataItem.setVersion(now.getTime());
                dataItemInfoMapper.updateById(updateDataItem);
            }
        }
        if (CollUtil.isNotEmpty(insertDataItemFields)) {
            for (IsDataItemField insertDataItemField : insertDataItemFields) {
                insertDataItemField.setVersion(now.getTime());
            }
            dataItemFieldMapper.insertBatch(insertDataItemFields);
        }
        if (CollUtil.isNotEmpty(updateDataItemFields)) {
            for (IsDataItemField updateDataItemField : updateDataItemFields) {
                updateDataItemField.setVersion(now.getTime());
                dataItemFieldMapper.updateById(updateDataItemField);
            }
        }
        if (CollUtil.isNotEmpty(insertControlCommands)) {
            for (IsProtocolControlCommand insertControlCommand : insertControlCommands) {
                insertControlCommand.setVersion(now.getTime());
            }
            controlCommandMapper.insertBatch(insertControlCommands);
        }
        if (CollUtil.isNotEmpty(updateControlCommands)) {
            for (IsProtocolControlCommand updateControlCommand : updateControlCommands) {
                updateControlCommand.setVersion(now.getTime());
                controlCommandMapper.updateById(updateControlCommand);
            }
        }
        if (CollUtil.isNotEmpty(insertControlParams)) {
            functionDetailInfoMapper.insertBatch(insertControlParams);
        }
        if (CollUtil.isNotEmpty(updateControlParams)) {
            for (IsEquipmentFunctionDetailInfo updateControlParam : updateControlParams) {
                functionDetailInfoMapper.updateById(updateControlParam);
            }
        }
        if (CollUtil.isNotEmpty(bitControlDataItemMap.values())) {
            Iterator<Map.Entry<Integer, IsDeviceProtocolDataItemInfo>> it = bitControlDataItemMap.entrySet().iterator();
            while (it.hasNext()) {
                Map.Entry<Integer, IsDeviceProtocolDataItemInfo> entry = it.next();
                IsDeviceProtocolDataItemInfo bitControlDataItem = entry.getValue();
                // 已存在的不新增
                if (bitControlDataItem.getIsDeleted() == 1) {
                    it.remove();
                    continue;
                }
                bitControlDataItem.setVersion(now.getTime());
                bitControlDataItem.setCrOper(username);
                bitControlDataItem.setGmtCreate(new Date());
                bitControlDataItem.setUpOper(username);
                bitControlDataItem.setGmtModified(new Date());
            }
            if (CollUtil.isNotEmpty(bitControlDataItemMap.values())) {
                dataItemInfoMapper.insertBatch(new HashSet<>(bitControlDataItemMap.values()));
            }
        }
        versionMapper.updateVersion(now.getTime());
    }

    private Map<String, String> getParamClassifies(String manufactureId,
            LinkedList<RegisterDataItem> registerDataItems) {
        Map<String, String> paramClassifies = new HashMap<>();
        final List<IsDictValueInfo> dictInfos = dictValueInfoMapper.selectList(new QueryWrapper<IsDictValueInfo>().select("title", "dic_no")
                .eq(IsDictValueInfo.TYPE_ID, "d_type_33")
                .eq(IsDictValueInfo.SFSC, 0)
                .eq(IsDictValueInfo.SFTY, 0)
                .eq(IsDictValueInfo.MANUFACTURE_ID, manufactureId));
        Set<String> inputClassifies = new HashSet<>();
        for (RegisterDataItem registerDataItem : registerDataItems) {
            if (StrUtil.isNotBlank(registerDataItem.getClassify())) {
                inputClassifies.add(registerDataItem.getClassify());
            }
        }
        for (IsDictValueInfo dict : dictInfos) {
            paramClassifies.put(dict.getTitle(), dict.getDicNo());
        }
        final Date date = new Date();
        for (String classify : inputClassifies) {
            if (!paramClassifies.containsKey(classify)) {
                IsDictValueInfo newDict = new IsDictValueInfo();
                newDict.setTypeId("d_type_33");
                newDict.setDicNo("xycsfl-" + cn.hutool.core.util.IdUtil.fastSimpleUUID());
                newDict.setTitle(classify);
                newDict.setBz(classify);
                newDict.setSfsc(0);
                newDict.setSfty(0);
                newDict.setManufactureId(manufactureId);
                newDict.setCrOper("system");
                newDict.setCjsj(date);
                newDict.setUpOper("system");
                newDict.setXgsj(date);
                newDict.setVersion(0L);
                newDict.setManufactureTemplate(1);
                newDict.setIsDefault(0);
                newDict.setSeq(0);
                newDict.setValueId("d_value_" + IdUtil.getIncreaseIdByNanoTime());
                newDict.setSjly("hot_chain");
                dictValueInfoMapper.insert(newDict);

                paramClassifies.put(classify, newDict.getDicNo());
            }
        }

        return paramClassifies;
    }

    private void checkExistsControlCommandDetail(List<NesProtocolDataSet> inputList, String protocolId) {
        int page = 0;
        int size = 1000;
        List<Long> dataItemIds = new ArrayList<>();
        // 获取标准项
        final List<Map<String, Object>> standardDataItems = standardDataItemMapper.getStandardFunctionOrignalInfo(protocolId);
        for (Map<String, Object> standardDataItem : standardDataItems) {
            String detailId = (String) standardDataItem.get("detailId");
            String name = (String) standardDataItem.get("name");
            String commandId = (String) standardDataItem.get("commandId");
            String functionCode = (String) standardDataItem.get("functionCode");
            String code = (String) standardDataItem.get("code");
            for (NesProtocolDataSet dataSet : inputList) {
                if (dataSet.getControlCommand() == null) {
                    continue;
                }
                String controlCommandId = dataSet.getControlCommand().getControlCommandId();
                if (!controlCommandId.equals(commandId) || CollUtil.isEmpty(dataSet.getControlCommandParams())) {
                    continue;
                }
                for (IsEquipmentFunctionDetailInfo detailInfo : dataSet.getControlCommandParams()) {
                    if (name.equals(detailInfo.getName())) {
                        detailInfo.setDetailId(detailId);
                        detailInfo.setFunctionCode(functionCode);
                        detailInfo.setCode(code);
                    }
                }
            }
        }

        while (true) {
            List<IsEquipmentFunctionDetailInfo> controlCommandDetailsDB = functionDetailInfoMapper.selectList(new QueryWrapper<IsEquipmentFunctionDetailInfo>().eq(IsEquipmentFunctionDetailInfo.PROTOCOL_ID, protocolId)
                    .eq(IsEquipmentFunctionDetailInfo.SFSC, 0)
                    .last("limit " + page * size + "," + size));

            if (CollUtil.isEmpty(controlCommandDetailsDB)) {
                break;
            }
            for (IsEquipmentFunctionDetailInfo controlCommandDetailDB : controlCommandDetailsDB) {
                for (NesProtocolDataSet dataSet : inputList) {
                    if (dataSet.getControlCommand() == null) {
                        continue;
                    }
                    String controlCommandId = dataSet.getControlCommand().getControlCommandId();
                    if (!controlCommandId.equals(controlCommandDetailDB.getControlCommandId()) || CollUtil.isEmpty(dataSet.getControlCommandParams())) {
                        continue;
                    }
                    controlCommandDetailDB.setSfsc(1);
                    for (IsEquipmentFunctionDetailInfo controlCommandDetail : dataSet.getControlCommandParams()) {
                        if (controlCommandDetailDB.getCode()
                                .equals(controlCommandDetail.getCode()) && controlCommandDetailDB.getFunctionCode()
                                .equals(controlCommandDetail.getFunctionCode()) && Objects.equals(controlCommandDetailDB.getValue(), controlCommandDetail.getValue())) {
                            controlCommandDetail.setDetailId(controlCommandDetailDB.getDetailId());
                            controlCommandDetail.setId(controlCommandDetailDB.getId());
                            controlCommandDetailDB.setSfsc(0);
                        }
                    }
                }
            }
            for (IsEquipmentFunctionDetailInfo controlCommandDetailDB : controlCommandDetailsDB) {
                if (controlCommandDetailDB.getSfsc() == 1) {
                    dataItemIds.add(controlCommandDetailDB.getId());
                }
            }
            page++;
        }
        if (CollUtil.isNotEmpty(dataItemIds)) {
            functionDetailInfoMapper.deleteBatchIds(dataItemIds);
        }
    }

    private void checkExistsControlCommand(List<NesProtocolDataSet> inputList, String protocolId) {
        int page = 0;
        int size = 1000;
        List<Long> ids = new ArrayList<>();
        List<String> controlCommandIds = new ArrayList<>();
        while (true) {
            List<IsProtocolControlCommand> controlCommandsDB = controlCommandMapper.selectList(new QueryWrapper<IsProtocolControlCommand>().eq(IsProtocolControlCommand.PROTOCOL_ID, protocolId)
                    .eq(IsProtocolControlCommand.IS_DELETED, 0)
                    .last("limit " + page * size + "," + size));
            if (CollUtil.isEmpty(controlCommandsDB)) {
                break;
            }
            for (IsProtocolControlCommand controlCommandDB : controlCommandsDB) {
                for (NesProtocolDataSet dataSet : inputList) {
                    // 通过关联字段关联旧的查询指令
                    if (controlCommandDB.getRelationDataItemIds().equals(dataSet.getDataItemInfo().getDataItemId())) {
                        if (dataSet.getControlCommand() == null) {
                            ids.add(controlCommandDB.getId());
                            controlCommandIds.add(controlCommandDB.getControlCommandId());
                            continue;
                        }
                        final String controlCommandId = controlCommandDB.getControlCommandId();
                        dataSet.getControlCommand().setControlCommandId(controlCommandId);
                        for (IsEquipmentFunctionDetailInfo controlCommandParam : dataSet.getControlCommandParams()) {
                            controlCommandParam.setControlCommandId(controlCommandId);
                        }
                        dataSet.getControlCommand().setId(controlCommandDB.getId());
                    }
                }
            }
            page++;
        }
        if (CollUtil.isNotEmpty(ids)) {
            controlCommandMapper.deleteBatchIds(ids);
        }
        if (CollUtil.isNotEmpty(controlCommandIds)) {
            functionDetailInfoMapper.delete(new QueryWrapper<IsEquipmentFunctionDetailInfo>().in(IsEquipmentFunctionDetailInfo.CONTROL_COMMAND_ID, controlCommandIds));
        }
    }

    private void checkExistsDataField(List<NesProtocolDataSet> inputList, String protocolId) {
        int page = 0;
        int size = 1000;
        List<Long> dataItemIds = new ArrayList<>();
        while (true) {
            List<IsDataItemField> dataItemFieldsDB = dataItemFieldMapper.selectList(new QueryWrapper<IsDataItemField>().eq(IsDataItemField.DEVICE_PROTOCOL_ID, protocolId)
                    .eq(IsDataItemField.IS_DELETED, 0)
                    .last("limit " + page * size + "," + size));
            if (CollUtil.isEmpty(dataItemFieldsDB)) {
                break;
            }
            for (IsDataItemField dataItemFieldDB : dataItemFieldsDB) {
                for (NesProtocolDataSet dataSet : inputList) {
                    if (dataItemFieldDB.getDataItemId().equals(dataSet.getDataItemInfo().getDataItemId())) {
                        dataItemFieldDB.setIsDeleted(1);
                        if (dataSet.getDataItemField() == null) {
                            continue;
                        }
                        for (IsDataItemField dataItemField : dataSet.getDataItemField()) {
                            if (dataItemFieldDB.getValue().equals(dataItemField.getValue())) {
                                dataItemField.setDataItemFieldId(dataItemFieldDB.getDataItemFieldId());
                                dataItemField.setId(dataItemFieldDB.getId());
                                dataItemFieldDB.setIsDeleted(0);
                            }
                        }
                    }
                }
            }
            for (IsDataItemField dataItemFieldDB : dataItemFieldsDB) {
                if (dataItemFieldDB.getIsDeleted() == 1) {
                    dataItemIds.add(dataItemFieldDB.getId());
                }
            }
            page++;
        }
        if (CollUtil.isNotEmpty(dataItemIds)) {
            dataItemFieldMapper.deleteBatchIds(dataItemIds);
        }
    }

    private IsDataItemTemplateInfo generateBitControlDataItem(IsDeviceProtocolDataItemInfo dataItemInfo,
            Map<Integer, IsDeviceProtocolDataItemInfo> bitControlDataItems, IsDataItemTemplateInfo lastTemplate) {
        // 查询数据库是否已存在
        if (bitControlDataItems.containsKey(dataItemInfo.getRegisterAddress())) {
            // 使用最小起始位
            Integer startBit = dataItemInfo.getStartByte();
            if (null != startBit && startBit < bitControlDataItems.get(dataItemInfo.getRegisterAddress())
                    .getStartByte()) {
                bitControlDataItems.get(dataItemInfo.getRegisterAddress()).setStartByte(startBit);
            }
            return lastTemplate;
        }
        IsDeviceProtocolDataItemInfo bitControlDataItem = new IsDeviceProtocolDataItemInfo();
        BeanUtil.copyProperties(dataItemInfo, bitControlDataItem);
        bitControlDataItem.setDataItemId("dpdi_" + IdUtil.getIncreaseIdByNanoTime());
        bitControlDataItem.setBitPlace(null);
        bitControlDataItem.setName("bit反控项" + dataItemInfo.getRegisterAddress());
        bitControlDataItem.setByteLength(16);
        bitControlDataItem.setConvertId(0);
        bitControlDataItem.setReasonableRangeLow(null);
        bitControlDataItem.setReasonableRangeUp(null);
        bitControlDataItem.setDataTypeEnum(DataType.UNSIGNED.name());
        bitControlDataItem.setIsDeleted(0);
        final IsDataItemTemplateInfo availableTemplate = getTemplateId(dataItemInfo.getProtocolId(), lastTemplate, true);
        String templateId = availableTemplate.getTemplateId();
        bitControlDataItem.setTemplateId(templateId);
        bitControlDataItem.setFormula(null);
        bitControlDataItems.put(dataItemInfo.getRegisterAddress(), bitControlDataItem);
        return availableTemplate;
    }

    /**
     * 获取控制命令
     * @param req 请求参数
     * @return {@link PageData}
     */
    public PageData getControlCommands(DataReq req) {
        int pageId = ParameterUtil.getInteger(req, "pageId", 1);
        int pageSize = ParameterUtil.getInteger(req, "pageSize", 20);
        String protocolId = ParameterUtil.getString(req, "protocolId");
        String name = ParameterUtil.getString(req, "name");
        Integer registerAddress = ParameterUtil.getInteger(req, "registerAddress");
        String registerType = ParameterUtil.getString(req, "registerType");

        Page<Map<String, Object>> page = new Page<>(pageId, pageSize);
        List<Map<String, Object>> list = controlCommandMapper.getControlCommand(page, protocolId, name, registerAddress, registerType);
        return PageData.ok(page, list);
    }

    public PageData deleteProtocol(DataReq req) {
        String protocolId = ParameterUtil.getString(req, "protocolId");
        if (StrUtil.isBlank(protocolId)) {
            return PageData.error("协议ID不能为空");
        }
        // 绑定了设备的协议不能删除
        final int equipmentCount = deviceProtocolInfoMapper.getEquipmentCount(protocolId);
        if (equipmentCount > 0) {
            return PageData.error("该协议已绑定设备，不能删除");
        }
        // 删除协议
        deviceProtocolInfoMapper.logicDeleteByBusinessKeyAndUpdateVersion(protocolId, System.currentTimeMillis());
        return PageData.ok();
    }

    /**
     * 保存标准数据项
     * @param req
     * @return
     */
    public PageData saveStandardDataItem(DataReq req) {
        final SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
        String protocolId;
        try {

            AStandardDataItemMapper standardDataItemMapperLocal = sqlSession.getMapper(AStandardDataItemMapper.class);

            IsDeviceProtocolDataItemInfoMapper dataItemInfoMapperLocal = sqlSession.getMapper(IsDeviceProtocolDataItemInfoMapper.class);

            IsEquipmentFunctionDetailInfoMapper functionDetailInfoMapperLocal = sqlSession.getMapper(IsEquipmentFunctionDetailInfoMapper.class);


            Date now = new Date();
            Map<String, StandardDataItemEnum> toStandard = new HashMap<>();
            Map<String, String> tempMap = new HashMap<>();

            protocolId = ParameterUtil.getString(req, "protocolId");
            String manufactureId = getProtocolManufactureId(protocolId, req);

            JSONArray standardDataItems = ParameterUtil.getJSONArray(req, "standardDataItems");
            if (StrUtil.isBlank(protocolId)) {
                return PageData.error("协议ID不能为空");
            }

            standardDataItemMapper.delete(new QueryWrapper<AStandardDataItem>().eq(AStandardDataItem.PROTOCOL_ID, protocolId));

            equipmentControlParamMapper.delete(new QueryWrapper<EquipmentControlParam>().eq(AStandardDataItem.PROTOCOL_ID, protocolId));

            dataItemInfoMapper.resumeDataItemTemplateId(protocolId);

            //            equipmentControlParamCustomMapper.delete(new QueryWrapper<EquipmentControlParamCustom>()
            //                    .eq(AStandardDataItem.PROTOCOL_ID, protocolId));

            // 恢复IsDeviceProtocolDataItemInfo被修改的template_id


            // 查询原有的标准数据项
            /*
            final List<AStandardDataItem> oldStandardDataItems =
                    standardDataItemMapper.selectList(new QueryWrapper<AStandardDataItem>()
                            .eq("protocol_id", protocolId));
            final Map<String, AStandardDataItem> oldStandardDataItemMap = new HashMap<>();
            for (AStandardDataItem oldStandardDataItem : oldStandardDataItems) {
                String key = oldStandardDataItem.getStandardDataItem();
                if (oldStandardDataItem.getDataItemValue() != null) {
                    key += "V" + oldStandardDataItem.getDataItemValue();
                }
                oldStandardDataItemMap.put(key, oldStandardDataItem);
            }*/

            // 提交的数据项
            ListIterator<Object> it = standardDataItems.listIterator();
            String SDI1_itemId = "";
            String SDI2_1_itemId = "";
            String SDI2_itemId = "";

            while (it.hasNext()) {
                JSONObject item = (JSONObject) it.next();
                String standardDataItem = item.getString("standardDataItem");
                String dataItemId = item.getString("dataItemId");
                String functionDetailId = item.getString("functionDetailId");
                if (StrUtil.isBlank(dataItemId) && StrUtil.isBlank(functionDetailId)) {
                    it.remove();
                }
                if (StrUtil.isBlank(standardDataItem)) {
                    it.remove();
                }
                if (StandardDataItemEnum.SDI3.name().equals(standardDataItem)) {
                    tempMap.put(functionDetailId, dataItemId);
                }
                if (StandardDataItemEnum.SDI2_1.name().equals(standardDataItem)) {
                    SDI2_1_itemId = dataItemId;
                }
                if (StandardDataItemEnum.SDI2.name().equals(standardDataItem)) {
                    SDI2_itemId = dataItemId;
                }
                if (StandardDataItemEnum.SDI1.name().equals(standardDataItem)) {
                    SDI1_itemId = dataItemId;
                }
            }

            // 保存a_standard_data_item表
            for (int i = 0; i < standardDataItems.size(); i++) {
                JSONObject item = standardDataItems.getJSONObject(i);
                String standardDataItem = item.getString("standardDataItem");
                String dataItemId = item.getString("dataItemId");
                String functionDetailId = item.getString("functionDetailId");
                Integer dataItemValue = item.getInteger("dataItemValue");

                String key = standardDataItem;
                if (key.equals(StandardDataItemEnum.SDI3.name()) && StrUtil.isBlank(functionDetailId)) {
                    // 根据数据项ID查询控制详情ID
                    // cn.com.nes.mybatis.agent.ope.mapper
                    // modify by zhouy 20240726
                    final String namespace = "cn.com.nes.mybatis.agent.ope.mapper.IsEquipmentFunctionDetailInfoMapper";
                    final String method = "getFunctionDetailIdByDataItemId";
                    functionDetailId = sqlSession.selectOne(namespace + "." + method, dataItemId);
                }
                if (dataItemValue != null) {
                    key += "V" + dataItemValue;
                }

                // 修改
                /*
                if (oldStandardDataItemMap.containsKey(key)) {
                    // 不去删除
                    final AStandardDataItem old = oldStandardDataItemMap.remove(key);
                    // 未修改
                    if (Objects.equals(old.getDataItemId(), dataItemId)
                            && Objects.equals(old.getFunctionDetailId(), functionDetailId)
                            && Objects.equals(old.getDataItemValue(), dataItemValue)) {
                        continue;
                    }
                    // 修改关联的数据项或控制详情
                    final StandardDataItemEnum standardDataItemEnum = StandardDataItemEnum.valueOf(standardDataItem);
                    if (StrUtil.isNotBlank(dataItemId) && !Objects.equals(old.getDataItemId(), dataItemId)) {
                        unStandard.put(old.getDataItemId(), standardDataItemEnum);
                        toStandard.put(dataItemId, standardDataItemEnum);
                    }

                    if (StrUtil.isNotBlank(functionDetailId) && !Objects.equals(old.getFunctionDetailId(), functionDetailId)) {
                        unStandard.put(old.getFunctionDetailId(), standardDataItemEnum);
                        toStandard.put(functionDetailId, standardDataItemEnum);
                    }

                    old.setDataItemId(dataItemId);
                    old.setFunctionDetailId(functionDetailId);
                    old.setDataItemValue(dataItemValue);
                    standardDataItemMapperLocal.updateById(old);

                } else {*/

                AStandardDataItem standardDataItemEntity = new AStandardDataItem();
                standardDataItemEntity.setManufactureId(manufactureId);
                standardDataItemEntity.setProtocolId(protocolId);
                standardDataItemEntity.setStandardDataItem(standardDataItem);
                standardDataItemEntity.setDataItemId(dataItemId);
                standardDataItemEntity.setFunctionDetailId(functionDetailId);
                standardDataItemEntity.setDataItemValue(dataItemValue);
                standardDataItemMapperLocal.insert(standardDataItemEntity);
                // 修改关联的数据项或控制详情
                //                    toStandard.put(StrUtil.blankToDefault(dataItemId, functionDetailId),
                //                            StandardDataItemEnum.valueOf(standardDataItem));

                //blankToDefault 作用 有dataItemId,没有就functionDetailId
                String key1 = StrUtil.blankToDefault(functionDetailId, dataItemId);
                if (StandardDataItemEnum.SDI2_1.name().equals(standardDataItem)) {
                    if (!toStandard.containsKey(key1)) {
                        toStandard.put(key1, StandardDataItemEnum.valueOf(standardDataItem));
                    }
                } else {
                    toStandard.put(key1, StandardDataItemEnum.valueOf(standardDataItem));
                }
                //}
            }

            boolean SDI2_1_flag = true;
            // 删除
            /*
            for (AStandardDataItem old : oldStandardDataItemMap.values()) {
                if(old.getStandardDataItem().equals(StandardDataItemEnum.SDI2_1.name())) {
                    if(StringUtils.isBlank(SDI2_1_itemId)) {
                        standardDataItemMapperLocal.deleteById(old.getId());
                    }else {
                        SDI2_1_flag = false;
                    }
                }else{
                    standardDataItemMapperLocal.deleteById(old.getId());
                }
                // 修改关联的数据项或控制详情
                StandardDataItemEnum standardDataItemEnum = StandardDataItemEnum.valueOf(old.getStandardDataItem());
                if (StrUtil.isNotBlank(old.getDataItemId())) {
                    unStandard.put(old.getDataItemId(), standardDataItemEnum);
                }
                if (StrUtil.isNotBlank(old.getFunctionDetailId())) {
                    unStandard.put(old.getFunctionDetailId(), standardDataItemEnum);
                }
            }
            */

            // 恢复为普通数据项
            /*
            if (unStandard.size() > 0) {
                // 恢复模板id
                List<String> l1 = new ArrayList<>();
                // 恢复控制详情code（按数据项详情id）
                List<String> l2 = new ArrayList<>();
                // 恢复控制详情code（按控制详情id）
                List<String> l3 = new ArrayList<>();

                for (Map.Entry<String, StandardDataItemEnum> entry : unStandard.entrySet()) {
                    String id = entry.getKey();
                    StandardDataItemEnum standardDataItemEnum = entry.getValue();
                    if (StrUtil.isNotBlank(standardDataItemEnum.getTemplateId())) {
                        l1.add(id);
                    }
                    if (StrUtil.isNotBlank(standardDataItemEnum.getCode())) {
                        String[] arr = standardDataItemEnum.getCode().split("_");
                        if (arr.length == 1) {
                            l2.add(id);
                        } else {
                            l3.add(id);
                        }
                    }
                }

                if (l1.size() > 0)
                {
                    Map<String, String> dataItemIdTemplateMap = getDataItemIdTemplateMap(protocolId, l1);
                    List<IsDeviceProtocolDataItemInfo> oldDataItemInfos = dataItemInfoMapper
                            .selectList(new QueryWrapper<IsDeviceProtocolDataItemInfo>()
                                    .eq("protocol_id", protocolId)
                                    .in(IsDeviceProtocolDataItemInfo.DATA_ITEM_ID, l1));
                    for (IsDeviceProtocolDataItemInfo oldDataItemInfo : oldDataItemInfos) {
                        oldDataItemInfo.setTemplateId(dataItemIdTemplateMap.get(oldDataItemInfo.getDataItemId()));
                        oldDataItemInfo.setVersion(now.getTime());
                        dataItemInfoMapperLocal.updateById(oldDataItemInfo);
                    }
                }

                List<Map<String, Object>> controlParams;
                if (l2.size() > 0) {
                    // 控制参数修改
                    controlParams = controlCommandMapper.getControlParamsByDataItemIds(l2);
                    for (Map<String, Object> controlParam : controlParams) {
                        final Long id = (Long) controlParam.get("id");
                        final Integer registerAddress = (Integer) controlParam.get("registerAddress");
                        final String registerType = (String) controlParam.get("registerType");
                        final RegisterTypeEnum registerTypeEnum = RegisterTypeEnum.valueOf(registerType);
                        String newCode = registerTypeEnum.getWriteFunctionCode() +
                                StrUtil.padPre(Integer.toHexString(registerAddress), 4, '0');
                        String code = (String) controlParam.get("code");
                        String functionCode = (String) controlParam.get("functionCode");
                        String subCode = functionCode.substring(code.length());
                        String newFunctionCode = newCode + subCode;
                        final IsEquipmentFunctionDetailInfo updateEntity = new IsEquipmentFunctionDetailInfo();
                        updateEntity.setId(id);
                        updateEntity.setCode(newCode);
                        updateEntity.setFunctionCode(newFunctionCode);
                        functionDetailInfoMapperLocal.updateById(updateEntity);
                    }
                }
                if (l3.size() > 0) {
                    controlParams = controlCommandMapper.getControlParamsByFunctionDetailIds(l3);
                    for (Map<String, Object> controlParam : controlParams)
                    {

                        final Long id = (Long) controlParam.get("id");
                        final String detailId = (String) controlParam.get("detailId");
                        final StandardDataItemEnum standardDataItemEnum = unStandard.get(detailId);
                        final Integer registerAddress = (Integer) controlParam.get("registerAddress");
                        final String registerType = (String) controlParam.get("registerType");
                        final RegisterTypeEnum registerTypeEnum = RegisterTypeEnum.valueOf(registerType);
                        String code = (String) controlParam.get("code");
                        String functionCode = (String) controlParam.get("functionCode");
                        String subCode = functionCode.substring(code.length());
                        String standardCode = standardDataItemEnum.getCode();
                        String[] arr = standardCode.split("_");
                        String newCode = registerTypeEnum.getWriteFunctionCode() +
                                StrUtil.padPre(Integer.toHexString(registerAddress), 4, '0');
                        final IsEquipmentFunctionDetailInfo updateEntity = new IsEquipmentFunctionDetailInfo();
                        updateEntity.setId(id);
                        updateEntity.setCode(newCode);
                        updateEntity.setFunctionCode(newCode + subCode);
                        if (arr.length == 2 && "xx".equalsIgnoreCase(arr[1])) {
                            updateEntity.setFunctionCode(newCode + "01");
                        }
                        functionDetailInfoMapperLocal.updateById(updateEntity);
                    }
                }

            }
            */

            // 修改为标准数据项
            if (toStandard.size() > 0) {

                List<String> l1 = new ArrayList<>();
                List<String> l2 = new ArrayList<>();
                List<String> l3 = new ArrayList<>();

                if (StringUtils.isNotBlank(SDI2_1_itemId) && StringUtils.isNotBlank(SDI2_itemId)) {
                    // 记录可写字段
                    l2.add(SDI2_1_itemId);
                }

                for (Map.Entry<String, StandardDataItemEnum> entry : toStandard.entrySet()) {
                    String id = entry.getKey();
                    StandardDataItemEnum standardDataItemEnum = entry.getValue();
                    if (StrUtil.isNotBlank(standardDataItemEnum.getTemplateId())) {
                        l1.add(id);
                    }
                    if (StrUtil.isNotBlank(standardDataItemEnum.getCode())) {
                        String[] arr = standardDataItemEnum.getCode().split("_");
                        if (arr.length == 2) {
                            l3.add(id);
                        }
                    }
                }

                // 所有有templateid的数据项 （dataItemInfoMapperLocal 更新setTemplateId）
                if (l1.size() > 0) {
                    List<IsDeviceProtocolDataItemInfo> oldDataItemInfos = dataItemInfoMapper.selectList(new QueryWrapper<IsDeviceProtocolDataItemInfo>().eq("protocol_id", protocolId)
                            .in(IsDeviceProtocolDataItemInfo.DATA_ITEM_ID, l1));

                    for (IsDeviceProtocolDataItemInfo oldDataItemInfo : oldDataItemInfos) {
                        final StandardDataItemEnum standardDataItemEnum = toStandard.get(oldDataItemInfo.getDataItemId());
                        oldDataItemInfo.setTemplateId(standardDataItemEnum.getTemplateId());
                        oldDataItemInfo.setVersion(now.getTime());
                        dataItemInfoMapperLocal.updateById(oldDataItemInfo);
                    }
                }

                // 模式
                List<Map<String, Object>> controlParams;
                if (l2.size() > 0) {
                    String controlCommandId = "";
                    // 控制参数修改(此处传入的dateitem要为可写)
                    controlParams = controlCommandMapper.getControlParamsByDataItemIds(l2);
                    for (Map<String, Object> controlParam : controlParams) {

                        final Long id = (Long) controlParam.get("id");
                        String dataItemId = (String) controlParam.get("dataItemId");
                        String code = (String) controlParam.get("code");
                        String functionCode = (String) controlParam.get("functionCode");
                        String functionName = (String) controlParam.get("functionName");
                        String subCode = functionCode.substring(code.length());
                        controlCommandId = (String) controlParam.get("controlCommandId");

                        StandardDataItemEnum standardDataItemEnum;
                        if (SDI2_1_itemId.equals(dataItemId)) {
                            standardDataItemEnum = StandardDataItemEnum.SDI2_1;
                        } else {
                            standardDataItemEnum = toStandard.get(dataItemId);
                        }
                        //  final StandardDataItemEnum standardDataItemEnum = toStandard.get(dataItemId);
                        if (standardDataItemEnum == null) {
                            continue;
                        }
                        final String standardCode = standardDataItemEnum.getCode();
                        if (standardCode.indexOf("_") > -1) {
                            String[] arr = standardCode.split("_");
                            String newCode = arr[0];
                            // add by zhouy
                            // 在字典表中查找
                            final List<IsDictValueInfo> dictInfos = dictValueInfoMapper.selectList(new QueryWrapper<IsDictValueInfo>().select("dic_no")
                                    .eq(IsDictValueInfo.TYPE_ID, "d_type_27")
                                    .isNotNull(IsDictValueInfo.P_VALUE_ID)
                                    .ne(IsDictValueInfo.P_VALUE_ID, "")
                                    .likeRight(IsDictValueInfo.DIC_NO, newCode)
                                    .eq(IsDictValueInfo.TITLE, functionName));

                            if (dictInfos.size() > 0) {
                                final IsEquipmentFunctionDetailInfo updateEntity = new IsEquipmentFunctionDetailInfo();
                                updateEntity.setId(id);
                                updateEntity.setCode(newCode);
                                updateEntity.setFunctionCode(dictInfos.get(0).getDicNo());
                                functionDetailInfoMapperLocal.updateById(updateEntity);

                                if (SDI2_1_flag) {
                                    IsEquipmentFunctionDetailInfo updateEntity1 = functionDetailInfoMapperLocal.selectOne(new LambdaQueryWrapper<>(updateEntity));
                                    // 添加标准字段
                                    AStandardDataItem standardDataItemEntity = new AStandardDataItem();
                                    standardDataItemEntity.setManufactureId(manufactureId);
                                    standardDataItemEntity.setProtocolId(protocolId);
                                    standardDataItemEntity.setStandardDataItem(StandardDataItemEnum.SDI2_1.name());
                                    standardDataItemEntity.setDataItemId(dataItemId);
                                    standardDataItemEntity.setFunctionDetailId(updateEntity1.getDetailId());
                                    standardDataItemEntity.setDataItemValue(updateEntity1.getValue().intValue());
                                    standardDataItemMapperLocal.insert(standardDataItemEntity);
                                }

                            } else {
                                final IsEquipmentFunctionDetailInfo updateEntity = new IsEquipmentFunctionDetailInfo();
                                updateEntity.setId(id);
                                updateEntity.setCode(newCode);
                                updateEntity.setFunctionCode(newCode + subCode);
                                functionDetailInfoMapperLocal.updateById(updateEntity);
                            }
                        } else {
                            String newCode = standardCode;
                            if (standardCode.length() > 1) {
                                newCode = standardCode.substring(0, 2);
                            }
                            final IsEquipmentFunctionDetailInfo updateEntity = new IsEquipmentFunctionDetailInfo();
                            updateEntity.setId(id);
                            updateEntity.setCode(newCode);
                            updateEntity.setFunctionCode(standardCode);
                            functionDetailInfoMapperLocal.updateById(updateEntity);
                        }
                    }

                    // 更新模式控制指令对应数据项
                    if (StringUtils.isNotBlank(controlCommandId)) {

                        if (!SDI2_itemId.equals(SDI2_1_itemId)) {

                            List<IsProtocolControlCommand> controlCommands = controlCommandMapper.selectList(new QueryWrapper<IsProtocolControlCommand>().eq(IsProtocolControlCommand.CONTROL_COMMAND_ID, controlCommandId)
                                    .eq(IsProtocolControlCommand.IS_DELETED, 0));

                            if (controlCommands != null && controlCommands.size() > 0) {
                                IsProtocolControlCommand isProtocolControlCommand = controlCommands.get(0);
                                isProtocolControlCommand.setIsQueryData(1);
                                isProtocolControlCommand.setControlData(SDI2_itemId);
                                //isProtocolControlCommand.setRelationDataItemIds(SDI2_itemId); // 查询的id
                                //isProtocolControlCommand.setRelationDataItemIds(SDI2_1_itemId);
                                isProtocolControlCommand.setVersion(now.getTime());
                                controlCommandMapper.updateById(isProtocolControlCommand);

                            }
                        }


                    }
                }

                // 控制数据项，01_01 或03_XX 类型
                if (l3.size() > 0) {
                    controlParams = controlCommandMapper.getControlParamsByFunctionDetailIds(l3);
                    int i = 1;
                    for (Map<String, Object> controlParam : controlParams) {

                        final Long id = (Long) controlParam.get("id");
                        final String detailId = (String) controlParam.get("detailId");
                        final String name = (String) controlParam.get("name");
                        final String dataitemid = (String) controlParam.get("dataItemId");
                        final StandardDataItemEnum standardDataItemEnum = toStandard.get(detailId);
                        final String code = (String) controlParam.get("code");
                        final String functionCode = (String) controlParam.get("functionCode");
                        //final String controlCommandId =  (String) controlParam.get("controlCommandId");

                        String subCode = functionCode.substring(code.length());
                        String standardCode = standardDataItemEnum.getCode();
                        String[] arr = standardCode.split("_");
                        String newCode = arr[0];
                        final IsEquipmentFunctionDetailInfo updateEntity = new IsEquipmentFunctionDetailInfo();
                        updateEntity.setId(id);
                        updateEntity.setCode(newCode);
                        if (arr.length == 2) {
                            subCode = arr[1];
                        }

                        // 查找对应的字段（控制指令）dataitemid
                        String tempQueryDataItemid = tempMap.get(detailId);
                        if (newCode.equals("03")) {
                            // 控制字段
                            if (name.contains("热水")) {
                                subCode = "03";
                            } else if (name.contains("热")) {
                                subCode = "02";
                            } else if (name.contains("冷")) {
                                subCode = "01";
                            } else if (name.contains("地暖")) {
                                subCode = "04";
                            } else {
                                subCode = "00";
                            }

                            // 修改成标准字段（把控制指令的字段修改成标准字段）
                            if (StringUtils.isNotBlank(tempQueryDataItemid)) {
                                List<IsDeviceProtocolDataItemInfo> tempDataItemInfos = dataItemInfoMapper.selectList(new QueryWrapper<IsDeviceProtocolDataItemInfo>().eq("protocol_id", protocolId)
                                        .eq(IsDeviceProtocolDataItemInfo.DATA_ITEM_ID, tempQueryDataItemid));
                                if (tempDataItemInfos != null && tempDataItemInfos.size() > 0) {
                                    IsDeviceProtocolDataItemInfo tempDataItemInfo = tempDataItemInfos.get(0);
                                    if (subCode.equals("03")) {
                                        tempDataItemInfo.setTemplateId("template_12624948623676529759229711015544646");
                                    } else if (subCode.equals("02")) {
                                        tempDataItemInfo.setTemplateId("template_12265115674536921759224686393746597");
                                    } else if (subCode.equals("01")) {
                                        tempDataItemInfo.setTemplateId("template_12265139300845591759224685295719172");
                                    } else if (subCode.equals("04")) {
                                        tempDataItemInfo.setTemplateId("template_12265634213057146759224681586270316");
                                    } else {
                                        tempDataItemInfo.setTemplateId("template_352");
                                    }
                                    tempDataItemInfo.setVersion(now.getTime());
                                    dataItemInfoMapperLocal.updateById(tempDataItemInfo);
                                }
                            }
                        } else if (newCode.equals("01")) {
                            tempQueryDataItemid = SDI1_itemId;
                            if (!tempQueryDataItemid.equals(dataitemid)) {

                                List<IsProtocolControlCommand> controlCommands = controlCommandMapper.selectList(new QueryWrapper<IsProtocolControlCommand>().eq(IsProtocolControlCommand.RELATION_DATA_ITEM_IDS, dataitemid)
                                        .eq(IsProtocolControlCommand.IS_DELETED, 0));
                                if (controlCommands != null && controlCommands.size() > 0) {
                                    IsProtocolControlCommand isProtocolControlCommand = controlCommands.get(0);
                                    isProtocolControlCommand.setIsQueryData(1);
                                    isProtocolControlCommand.setControlData(tempQueryDataItemid);
                                    isProtocolControlCommand.setVersion(now.getTime());
                                    controlCommandMapper.updateById(isProtocolControlCommand);
                                }
                            }

                        }

                        // 查询字段
                        updateEntity.setFunctionCode(newCode + subCode);
                        functionDetailInfoMapperLocal.updateById(updateEntity);

                        // 查询tempQueryDataItemid的控制指令
                        // 修改控制指令关联的数据项
                        if (newCode.equals("03")) {
                            if (StringUtils.isNotBlank(tempQueryDataItemid)) {
                                if (!tempQueryDataItemid.equals(dataitemid)) {
                                    List<IsProtocolControlCommand> controlCommands = controlCommandMapper.selectList(new QueryWrapper<IsProtocolControlCommand>().eq(IsProtocolControlCommand.RELATION_DATA_ITEM_IDS, dataitemid)
                                            .eq(IsProtocolControlCommand.IS_DELETED, 0));
                                    if (controlCommands != null && controlCommands.size() > 0) {
                                        IsProtocolControlCommand isProtocolControlCommand = controlCommands.get(0);
                                        isProtocolControlCommand.setIsQueryData(1);
                                        isProtocolControlCommand.setControlData(tempQueryDataItemid);
                                        //                                    isProtocolControlCommand.setRelationDataItemIds(tempQueryDataItemid);
                                        isProtocolControlCommand.setVersion(now.getTime());
                                        controlCommandMapper.updateById(isProtocolControlCommand);
                                    }
                                }
                            }
                        }
                    }
                }
            }
            sqlSession.commit();

        } catch (Exception e) {
            sqlSession.rollback();
            sqlSession.close();
            throw new RuntimeException(e);
        } finally {
            if (sqlSession != null) {
                sqlSession.close();
            }
        }
        generateEquipmentControlParam(protocolId);
        return PageData.ok();
    }

    private Map<String, String> getDataItemIdTemplateMap(String protocolId, List<String> dataItemIds) {
        List<IsDeviceProtocolDataItemInfo> dataItemInfos = dataItemInfoMapper.selectList(new QueryWrapper<IsDeviceProtocolDataItemInfo>().eq("protocol_id", protocolId)
                .in(IsDeviceProtocolDataItemInfo.DATA_ITEM_ID, dataItemIds));
        LinkedList<RegisterDataItem> registerDataItems = new LinkedList<>();
        for (IsDeviceProtocolDataItemInfo dataItemInfo : dataItemInfos) {
            registerDataItems.add(new RegisterDataItem(dataItemInfo));
        }
        final Map<String, String> registerTemplateMap = getRegisterTemplateMap(protocolId, registerDataItems);
        Map<String, String> dataItemIdTemplateMap = new HashMap<>();
        for (RegisterDataItem registerDataItem : registerDataItems) {
            final StringBuilder key = new StringBuilder(registerDataItem.getRegisterType()).append("_")
                    .append(registerDataItem.getRegisterAddress());
            if (registerDataItem.getBitAddress() != null) {
                key.append("_").append(registerDataItem.getBitAddress());
            }
            dataItemIdTemplateMap.put(registerDataItem.getDataItemId(), registerTemplateMap.get(key.toString()));
        }
        return dataItemIdTemplateMap;
    }

    public void generateEquipmentControlParam(String protocolId) {
        SqlSession sqlSession = null;
        try {
            // 查询控制指令
            final List<ControlCommandForEquipmentControlParam> controlCommands = controlCommandMapper.getControlCommandForEquipmentControlParam(protocolId);

            // 查询详情
            final List<IsEquipmentFunctionDetailInfo> controlCommandDetails = functionDetailInfoMapper.selectByProtocolId(protocolId);

            Map<String, List<IsEquipmentFunctionDetailInfo>> controlCommandDetailMap = new HashMap<>();

            for (IsEquipmentFunctionDetailInfo detail : controlCommandDetails) {
                if (!controlCommandDetailMap.containsKey(detail.getControlCommandId())) {
                    controlCommandDetailMap.put(detail.getControlCommandId(), new ArrayList<>());
                }
                controlCommandDetailMap.get(detail.getControlCommandId()).add(detail);
            }


            // 生成参数表
            List<EquipmentControlParam> insertList = new ArrayList<>();
            ControlCommandForEquipmentControlParam modeParam = null;
            List<ControlCommandForEquipmentControlParam> setTempParams = new ArrayList<>();
            List<ControlCommandForEquipmentControlParam> switchParams = new ArrayList<>();

            // 查询所有的控制指令
            for (ControlCommandForEquipmentControlParam command : controlCommands) {

                if (controlCommandDetailMap.containsKey(command.getControlCommandId())) {
                    command.setDetailInfoList(controlCommandDetailMap.get(command.getControlCommandId()));
                }

                // 得到控制指令的参数信息
                final List<EquipmentControlParam> equipmentControlParams = command.toEquipmentControlParam();
                for (EquipmentControlParam e : equipmentControlParams) {
                    e.setControlCommandId(command.getControlCommandId());
                    e.setGroupCode(command.getClassify());
                    e.setGroupName(command.getClassify());
                    // 单独处理01（开关机）和02（模式）的参数
                    String functionCode = e.getFunctionCode();
                    if (functionCode != null && functionCode.length() < 5) {
                        String code;
                        if (functionCode.length() == 2) {
                            code = functionCode;
                        } else {
                            code = functionCode.substring(0, 2);
                        }
                        switch (code) {
                            case "02":
                                modeParam = command;
                                continue;
                            case "01":
                                switchParams.add(command);
                                continue;
                            case "03":
                                setTempParams.add(command);
                                break;
                        }

                    }
                    insertList.add(e);
                }
            }

            // 开关机
            if (switchParams.size() > 0) {
                // 第一条
                final ControlCommandForEquipmentControlParam command = switchParams.get(0);
                // 第二条及以后
                // 合并开关机详情
                if (switchParams.size() > 1) {
                    for (int i = 1; i < switchParams.size(); i++) {
                        command.getDetailInfoList().addAll(switchParams.get(i).getDetailInfoList());
                    }
                }
                final ListIterator<IsEquipmentFunctionDetailInfo> it = command.getDetailInfoList().listIterator();
                while (it.hasNext()) {
                    final IsEquipmentFunctionDetailInfo detail = it.next();
                    if (!detail.getCode().equals("01")) {
                        it.remove();
                    }
                }
                command.setFieldName(StandardDataItemEnum.SDI1.getField());
                insertList.addAll(command.toEquipmentControlParam());
            }

            // 模式和设定温度
            if (modeParam != null) {
                final EquipmentControlParam modeControlParam;
                if (setTempParams.size() > 0) {
                    final List<AStandardDataItem> setTempStandardDataItem = standardDataItemMapper.selectSetTempControlParams(protocolId);
                    modeControlParam = modeParam.addSetTemp(setTempParams, setTempStandardDataItem);
                } else {
                    modeControlParam = modeParam.toEquipmentControlParam().get(0);
                }
                insertList.add(modeControlParam);
            }


            if (insertList.size() == 0) {
                return;
            }
            sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
            sqlSession.getMapper(EquipmentControlParamMapper.class)
                    .delete(new QueryWrapper<EquipmentControlParam>().eq(EquipmentControlParam.PROTOCOL_ID, protocolId));
            for (EquipmentControlParam equipmentControlParam : insertList) {
                sqlSession.getMapper(EquipmentControlParamMapper.class).insert(equipmentControlParam);
            }
            sqlSession.commit();
        } catch (Exception e) {
            if (sqlSession != null) {
                sqlSession.rollback();
            }
            log.error("{}", e);
            throw new RuntimeException(e);
        } finally {
            if (sqlSession != null) {
                sqlSession.close();
            }
        }
    }

    public PageData getStandardDataItems(DataReq req) {

        String protocolId = ParameterUtil.getString(req, "protocolId");
        String manufacturerId = getProtocolManufactureId(protocolId, req);
        String protocolName = ParameterUtil.getString(req, "protocolName");
        if (StrUtil.isBlank(protocolId)) {
            return PageData.error("协议ID不能为空");
        }

        LinkedList<GetStandardDataItemsDTO> list = standardDataItemMapper.getStandardDataItems(manufacturerId, protocolId, protocolName);
        // 处理名称
        for (GetStandardDataItemsDTO dto : list) {
            dto.setStandardDataItemName(StandardDataItemEnum.valueOf(dto.getStandardDataItem()).getName());
        }
        // 处理开关机
        GetStandardDataItemsDTO switchDto = null;
        List<GetStandardDataItemsDTO> switchCommandList = new ArrayList<>();
        // 处理模式
        GetStandardDataItemsDTO modeDto = null;
        List<GetStandardDataItemsDTO> setTempCommandList = new ArrayList<>();
        for (GetStandardDataItemsDTO dto : list) {
            if (StandardDataItemEnum.SDI1.name().equals(dto.getStandardDataItem())) {
                switchDto = dto;
            } else if (StandardDataItemEnum.SDI1_1.name()
                    .equals(dto.getStandardDataItem()) || StandardDataItemEnum.SDI1_2.name()
                    .equals(dto.getStandardDataItem())) {
                switchCommandList.add(dto);
            } else if (StandardDataItemEnum.SDI2.name().equals(dto.getStandardDataItem())) {
                modeDto = dto;
            } else if (StandardDataItemEnum.SDI3.name().equals(dto.getStandardDataItem())) {
                setTempCommandList.add(dto);
            }
        }
        if (switchDto != null && switchCommandList.size() > 0) {
            switchDto.setChild(switchCommandList);
            list.removeAll(switchCommandList);
        }
        // 查询模式详情
        if (modeDto != null && setTempCommandList.size() > 0) {
            String dataItemId = modeDto.getDataItemId();
            List<IsDataItemField> dataItemFields = dataItemFieldMapper.selectList(new QueryWrapper<IsDataItemField>().select(IsDataItemField.VALUE, IsDataItemField.NAME)
                    .eq(IsDataItemField.DATA_ITEM_FIELD_ID, dataItemId)
                    .eq(IsDataItemField.IS_DELETED, 0));
            for (IsDataItemField dataItemField : dataItemFields) {
                for (GetStandardDataItemsDTO dto : setTempCommandList) {
                    if (dataItemField.getValue().equals(dto.getDataItemValue())) {
                        dto.setStandardDataItemName(dataItemField.getName() + "-" + dto.getStandardDataItemName());
                    }
                }
            }
            modeDto.setChild(setTempCommandList);
            list.removeAll(setTempCommandList);
        }

        Collections.sort(list, new Comparator<GetStandardDataItemsDTO>() {
            @Override
            public int compare(GetStandardDataItemsDTO o1, GetStandardDataItemsDTO o2) {
                return o1.getStandardDataItem().compareTo(o2.getStandardDataItem());
            }
        });

        return PageData.ok(list, true);
    }

    public PageData getDataItemsForDropdown(DataReq req) {
        String protocolId = ParameterUtil.getString(req, "protocolId");
        String manufacturerId = getProtocolManufactureId(protocolId, req);
        String dataItemName = ParameterUtil.getString(req, "dataItemName");
        Integer checkFault = ParameterUtil.getInteger(req, "checkFault");

        if (StrUtil.isBlank(protocolId)) {
            return PageData.error("协议ID不能为空");
        }
        List<DataItemsForDropdownDTO> page = dataItemInfoMapper.getDataItemsForDropdown(manufacturerId, protocolId, dataItemName, checkFault);
        List<String> dataItemIds = new ArrayList<>();
        if (StrUtil.isNotBlank(dataItemName)) {
            for (DataItemsForDropdownDTO dto : page) {
                dataItemIds.add(dto.getDataItemId());
            }
        }
        // 详情
        List<IsDataItemField> dataItemFields = dataItemFieldMapper.selectList(new QueryWrapper<IsDataItemField>().select(IsDataItemField.DATA_ITEM_ID, IsDataItemField.VALUE, IsDataItemField.NAME)
                .eq(IsDataItemField.IS_DELETED, 0)
                .eq(IsDataItemField.DEVICE_PROTOCOL_ID, protocolId)
                .in(dataItemIds.size() > 0, IsDataItemField.DATA_ITEM_ID, dataItemIds));
        Map<String, List<IsDataItemField>> dataItemFieldMap = new HashMap<>();
        for (IsDataItemField dataItemField : dataItemFields) {
            List<IsDataItemField> details = dataItemFieldMap.get(dataItemField.getDataItemId());
            if (details == null) {
                details = new ArrayList<>();
                dataItemFieldMap.put(dataItemField.getDataItemId(), details);
            }
            details.add(dataItemField);
        }
        for (DataItemsForDropdownDTO dto : page) {
            if (dataItemFieldMap.containsKey(dto.getDataItemId())) {
                final List<IsDataItemField> details = dataItemFieldMap.get(dto.getDataItemId());
                final List<DataItemDetailDTO> detailDTOs = new ArrayList<>();
                for (IsDataItemField detail : details) {
                    DataItemDetailDTO detailDTO = new DataItemDetailDTO();
                    detailDTO.setValue(Integer.valueOf(detail.getValue()));
                    detailDTO.setName(detail.getName());
                    detailDTOs.add(detailDTO);
                }
                dto.setDetails(detailDTOs);
            }
        }

        // 按照dataItemName匹配度排序
        final CompareStringMatch compareStringMatch = new CompareStringMatch(dataItemName);
        Collections.sort(page, new Comparator<DataItemsForDropdownDTO>() {
            @Override
            public int compare(DataItemsForDropdownDTO o1, DataItemsForDropdownDTO o2) {
                String s1 = o1.getDataItemName();
                String s2 = o2.getDataItemName();
                return compareStringMatch.compare(s1, s2);
            }
        });

        return PageData.ok(page, true);
    }

    public PageData getControlCommandForDropdown(DataReq req) {
        final String protocolId = ParameterUtil.getString(req, "protocolId");
        final String manufactureId = getProtocolManufactureId(protocolId, req);
        final String functionName = ParameterUtil.getString(req, "functionName");
        if (StrUtil.isBlank(protocolId)) {
            return PageData.error("协议ID不能为空");
        }
        List<IsEquipmentFunctionDetailInfo> functionDetails = functionDetailInfoMapper.getEquipmentFunctionDetailInfo(manufactureId, functionName, protocolId);

        final List<FunctionDetailDTO> page = new ArrayList<>();
        for (IsEquipmentFunctionDetailInfo detail : functionDetails) {
            FunctionDetailDTO detailDTO = new FunctionDetailDTO();
            detailDTO.setDetailId(detail.getDetailId());
            detailDTO.setName(detail.getName());
            page.add(detailDTO);
        }

        // 按照commandName匹配度排序
        final CompareStringMatch compareStringMatch = new CompareStringMatch(functionName);
        Collections.sort(page, new Comparator<FunctionDetailDTO>() {
            @Override
            public int compare(FunctionDetailDTO o1, FunctionDetailDTO o2) {
                String s1 = o1.getName();
                String s2 = o2.getName();
                return compareStringMatch.compare(s1, s2);
            }

        });

        return PageData.ok(page, true);
    }

    public Map<String, String> getRegisterTemplateMap(String protocolId,
            LinkedList<RegisterDataItem> registerDataItems) {

        final List<RegisterTemplateMap> registerTemplateMaps = registerTemplateMapMapper.selectList(new QueryWrapper<RegisterTemplateMap>().eq(RegisterTemplateMap.PROTOCOL_ID, protocolId));

        final Map<String, String> registerTemplateMap = new HashMap<>();
        for (RegisterTemplateMap map : registerTemplateMaps) {
            StringBuilder key = new StringBuilder(map.getRegisterType()).append("_").append(map.getRegisterAddress());
            if (map.getBitPlace() != null) {
                key.append("_").append(map.getBitPlace());
            }
            registerTemplateMap.put(key.toString(), map.getTemplateId());
        }
        List<RegisterTemplateMap> unmatched = new ArrayList<>();
        for (RegisterDataItem registerDataItem : registerDataItems) {
            StringBuilder key = new StringBuilder(registerDataItem.getRegisterType()).append("_")
                    .append(registerDataItem.getRegisterAddress());
            if (registerDataItem.getBitAddress() != null) {
                key.append("_").append(registerDataItem.getBitAddress());
            }
            if (!registerTemplateMap.containsKey(key.toString())) {
                final RegisterTemplateMap e = new RegisterTemplateMap();
                unmatched.add(e);
                e.setRegisterType(registerDataItem.getRegisterType());
                e.setRegisterAddress(registerDataItem.getRegisterAddress());
                // modify by zhouy
                e.setBitPlace(registerDataItem.getBitPlace());
                e.setBitPlaceEnd(registerDataItem.getBitPlaceEnd());
                e.setBitAddress(registerDataItem.getBitAddress());
                e.setProtocolId(protocolId);
            }
        }
        if (!unmatched.isEmpty()) {
            final LinkedList<IsDataItemTemplateInfo> availableTemplates = getAvailableTemplates(protocolId, unmatched.size());
            for (int i = 0; i < unmatched.size(); i++) {
                final RegisterTemplateMap map = unmatched.get(i);
                final IsDataItemTemplateInfo template = availableTemplates.get(i);
                map.setTemplateId(template.getTemplateId());
                map.setFieldName(template.getFieldName());
                StringBuilder key = new StringBuilder(map.getRegisterType()).append("_")
                        .append(map.getRegisterAddress());
                //                if (map.getBitPlace() != null) {
                //                    key.append("_").append(map.getBitPlace());
                //                }

                if (map.getBitAddress() != null) {
                    key.append("_").append(map.getBitAddress());
                }
                registerTemplateMap.put(key.toString(), template.getTemplateId());
            }
            for (RegisterTemplateMap map : unmatched) {
                registerTemplateMapMapper.delete(new QueryWrapper<RegisterTemplateMap>().eq(RegisterTemplateMap.PROTOCOL_ID, protocolId)
                        .eq(RegisterTemplateMap.REGISTER_TYPE, map.getRegisterType())
                        .eq(RegisterTemplateMap.REGISTER_ADDRESS, map.getRegisterAddress())
                        .eq(RegisterTemplateMap.BIT_PLACE, map.getBitPlace()));
                registerTemplateMapMapper.insert(map);
            }
        }
        // 处理标准字段
        final List<Map<String, Object>> standardDataItems = standardDataItemMapper.getStandardDataItemTemplate(protocolId);
        for (Map<String, Object> map : standardDataItems) {
            StringBuilder key = new StringBuilder((String) map.get("registerType")).append("_")
                    .append(map.get("registerAddress"));
            if (map.get("bitPlace") != null) {
                key.append("_").append(map.get("bitPlace"));
            }
            String standardDataItemName = (String) map.get("standardDataItem");
            if (StandardDataItemEnum.SDI3.name().equals(standardDataItemName)) {
                String dataItemId = (String) map.get("dataItemId");
                String templateId = standardDataItemMapper.getStandardTemplate(protocolId, dataItemId);
                registerTemplateMap.put(key.toString(), templateId);
                // 从别处查询
            } else if (StandardDataItemEnum.SDI2_1.name().equals(standardDataItemName)) {
                String dataItemId = (String) map.get("dataItemId");
                String templateId = standardDataItemMapper.getStandardTemplate(protocolId, dataItemId);
                registerTemplateMap.put(key.toString(), templateId);
            } else {
                registerTemplateMap.put(key.toString(), StandardDataItemEnum.valueOf(standardDataItemName)
                        .getTemplateId());
            }
        }
        return registerTemplateMap;
    }

    public PageData getClassify(DataReq req) {
        String protocolId = ParameterUtil.getString(req, "protocolId");
        String manufactureId = getProtocolManufactureId(protocolId, req);
        List<Map<String, Object>> dictValueInfos = dictValueInfoMapper.selectMaps(new QueryWrapper<IsDictValueInfo>().select("title")
                .eq(IsDictValueInfo.TYPE_ID, "d_type_33")
                .eq(IsDictValueInfo.MANUFACTURE_ID, manufactureId)
                .eq(IsDictValueInfo.SFTY, "0")
                .eq(IsDictValueInfo.SFSC, "0"));
        return PageData.ok(dictValueInfos);
    }

    public String getProtocolManufactureId(String protocolId, DataReq req) {
        String companyType = req.getUinfo().getCompanyType();
        if ("01".equals(companyType)) {
            return req.getUinfo().getNes_companyId();
        }
        return getProtocolManufactureId(protocolId);
    }

    public String getProtocolManufactureId(String protocolId) {
        final List<IsDeviceProtocolInfo> results = deviceProtocolInfoMapper.selectList(new QueryWrapper<IsDeviceProtocolInfo>().eq(IsDeviceProtocolInfo.PROTOCOL_ID, protocolId));
        return CollUtil.isNotEmpty(results) ? results.get(0).getManufactureId() : null;
    }

    public PageData getManufactures(DataReq req) {
        String companyId = req.getUinfo().getCompanyId();
        agentFacturerMapper.selectList(new QueryWrapper<TAgentFacturer>().select("")
                .eq(TAgentFacturer.DELETED, "0")
                .eq(TAgentFacturer.AGENTID, companyId));
        return null;
    }

    public PageData getProtocolFaultDataItemRelation(DataReq req) {
        String protocolId = ParameterUtil.getString(req, "protocolId");
        int pageId = ParameterUtil.getInteger(req, "pageId", 1);
        int pageSize = ParameterUtil.getInteger(req, "pageSize", 20);
        String faultName = ParameterUtil.getString(req, "faultName");

        Page<Map<String, Object>> page = new Page<>(pageId, pageSize);
        List<Map<String, Object>> result = faultDataItemRelationMapper.getProtocolFaultDataItemRelation(page, protocolId, faultName);
        for (Map<String, Object> map : result) {
            String relatedDataItemName = (String) map.get("relatedDataItemName");
            String relatedDataItemId = (String) map.get("relatedDataItemId");
            // split comma
            List<String> relatedDataItemNames = StrUtil.split(relatedDataItemName, ',');
            List<String> relatedDataItemIds = StrUtil.split(relatedDataItemId, ',');

            List<Map<String, Object>> relatedDataItems = new ArrayList<>();
            for (int i = 0; i < relatedDataItemNames.size(); i++) {
                Map<String, Object> relatedDataItem = new HashMap<>();
                relatedDataItem.put("relatedDataItemName", relatedDataItemNames.get(i));
                relatedDataItem.put("relatedDataItemId", relatedDataItemIds.get(i));
                relatedDataItems.add(relatedDataItem);
            }
            map.put("relatedDataItems", relatedDataItems);

            map.remove("relatedDataItemName");
            map.remove("relatedDataItemId");
        }
        page.setRecords(result);
        return PageData.ok(page);
    }


    @OpeTransactional
    public PageData saveProtocolFaultDataItemRelation(DataReq req) {
        ProtocolFaultDataItemRelationVo vo = ParameterUtil.parse(req, ProtocolFaultDataItemRelationVo.class);
        String protocolId = vo.getProtocolId();
        String faultDataItemId = vo.getFaultDataItemId();
        List<String> relatedDataItemIds = vo.getRelatedDataItemIds();

        // 删除原有关系
        faultDataItemRelationMapper.delete(new QueryWrapper<FaultDataItemRelation>().eq(FaultDataItemRelation.PROTOCOL_ID, protocolId)
                .eq(FaultDataItemRelation.FAULT_DATA_ITEM_ID, faultDataItemId));

        // 保存新关系
        for (String relatedDataItemId : relatedDataItemIds) {
            FaultDataItemRelation relation = new FaultDataItemRelation();
            relation.setProtocolId(protocolId);
            relation.setFaultDataItemId(faultDataItemId);
            relation.setRelatedDataItemId(relatedDataItemId);
            faultDataItemRelationMapper.insert(relation);
        }
        return PageData.ok();
    }

    @OpeTransactional
    public PageData deleteProtocolFaultDataItemRelation(DataReq req) {
        String faultDataItemId = ParameterUtil.getString(req, "faultDataItemId");
        faultDataItemRelationMapper.delete(new QueryWrapper<FaultDataItemRelation>().eq(FaultDataItemRelation.FAULT_DATA_ITEM_ID, faultDataItemId));
        return PageData.ok();
    }

    public static class CommandResponse {

        private String responseHeader;

        private int bitLength;

        public String getResponseHeader() {
            return responseHeader;
        }

        public void setResponseHeader(String responseHeader) {
            this.responseHeader = responseHeader;
        }

        public int getBitLength() {
            return bitLength;
        }

        public void setBitLength(int bitLength) {
            this.bitLength = bitLength;
        }

    }

}
